diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/ApiTest.js | 252 | ||||
| -rw-r--r-- | test/Base64Test.js | 5 | ||||
| -rw-r--r-- | test/BinderTest.js | 1001 | ||||
| -rw-r--r-- | test/ConsoleTest.js | 13 | ||||
| -rw-r--r-- | test/ControlBarTest.js | 2 | ||||
| -rw-r--r-- | test/DataStoreTest.js | 617 | ||||
| -rw-r--r-- | test/EntityDeclarationTest.js | 46 | ||||
| -rw-r--r-- | test/FileControllerTest.js | 98 | ||||
| -rw-r--r-- | test/FiltersTest.js | 153 | ||||
| -rw-r--r-- | test/JsonTest.js | 69 | ||||
| -rw-r--r-- | test/LoaderTest.js | 70 | ||||
| -rw-r--r-- | test/ModelTest.js | 84 | ||||
| -rw-r--r-- | test/ParserTest.js | 462 | ||||
| -rw-r--r-- | test/ScopeTest.js | 144 | ||||
| -rw-r--r-- | test/ServerTest.js | 42 | ||||
| -rw-r--r-- | test/UsersTest.js | 26 | ||||
| -rw-r--r-- | test/ValidatorsTest.js | 65 | ||||
| -rw-r--r-- | test/WidgetsTest.js | 269 | ||||
| -rw-r--r-- | test/XSitePostTest.js | 47 | ||||
| -rw-r--r-- | test/formsTest.js | 22 | ||||
| -rw-r--r-- | test/test/StepsTest.js | 7 | ||||
| -rw-r--r-- | test/testabilityPatch.js | 129 |
22 files changed, 3623 insertions, 0 deletions
diff --git a/test/ApiTest.js b/test/ApiTest.js new file mode 100644 index 00000000..250a27b1 --- /dev/null +++ b/test/ApiTest.js @@ -0,0 +1,252 @@ +ApiTest = TestCase("ApiTest"); + +ApiTest.prototype.testItShouldReturnTypeOf = function (){ + assertEquals("undefined", angular.Object.typeOf(undefined)); + assertEquals("null", angular.Object.typeOf(null)); + assertEquals("object", angular.Collection.typeOf({})); + assertEquals("array", angular.Array.typeOf([])); + assertEquals("string", angular.Object.typeOf("")); + assertEquals("date", angular.Object.typeOf(new Date())); + assertEquals("element", angular.Object.typeOf(document.body)); + assertEquals("function", angular.Object.typeOf(function(){})); +}; + +ApiTest.prototype.testItShouldReturnSize = function(){ + assertEquals(0, angular.Collection.size({})); + assertEquals(1, angular.Collection.size({a:"b"})); + assertEquals(0, angular.Object.size({})); + assertEquals(1, angular.Array.size([0])); +}; + +ApiTest.prototype.testIncludeIf = function() { + var array = []; + var obj = {}; + + angular.Array.includeIf(array, obj, true); + angular.Array.includeIf(array, obj, true); + assertTrue(_.include(array, obj)); + assertEquals(1, array.length); + + angular.Array.includeIf(array, obj, false); + assertFalse(_.include(array, obj)); + assertEquals(0, array.length); + + angular.Array.includeIf(array, obj, 'x'); + assertTrue(_.include(array, obj)); + assertEquals(1, array.length); + angular.Array.includeIf(array, obj, ''); + assertFalse(_.include(array, obj)); + assertEquals(0, array.length); +}; + +ApiTest.prototype.testSum = function(){ + assertEquals(3, angular.Array.sum([{a:"1"}, {a:"2"}], 'a')); +}; + +ApiTest.prototype.testSumContainingNaN = function(){ + assertEquals(1, angular.Array.sum([{a:1}, {a:Number.NaN}], 'a')); + assertEquals(1, angular.Array.sum([{a:1}, {a:Number.NaN}], function($){return $.a;})); +}; + +ApiTest.prototype.testInclude = function(){ + assertTrue(angular.Array.include(['a'], 'a')); + assertTrue(angular.Array.include(['a', 'b'], 'a')); + assertTrue(!angular.Array.include(['c'], 'a')); + assertTrue(!angular.Array.include(['c', 'b'], 'a')); +}; + +ApiTest.prototype.testIndex = function(){ + assertEquals(angular.Array.indexOf(['a'], 'a'), 0); + assertEquals(angular.Array.indexOf(['a', 'b'], 'a'), 0); + assertEquals(angular.Array.indexOf(['b', 'a'], 'a'), 1); + assertEquals(angular.Array.indexOf(['b', 'b'],'x'), -1); +}; + +ApiTest.prototype.testRemove = function(){ + var items = ['a', 'b', 'c']; + assertEquals(angular.Array.remove(items, 'q'), 'q'); + assertEquals(items.length, 3); + + assertEquals(angular.Array.remove(items, 'b'), 'b'); + assertEquals(items.length, 2); + + assertEquals(angular.Array.remove(items, 'a'), 'a'); + assertEquals(items.length, 1); + + assertEquals(angular.Array.remove(items, 'c'), 'c'); + assertEquals(items.length, 0); + + assertEquals(angular.Array.remove(items, 'q'), 'q'); + assertEquals(items.length, 0); +}; + +ApiTest.prototype.testFindById = function() { + var items = [{$id:1}, {$id:2}, {$id:3}]; + assertNull(angular.Array.findById(items, 0)); + assertEquals(items[0], angular.Array.findById(items, 1)); + assertEquals(items[1], angular.Array.findById(items, 2)); + assertEquals(items[2], angular.Array.findById(items, 3)); +}; + +ApiTest.prototype.testFilter = function() { + var items = ["MIsKO", {name:"shyam"}, ["adam"], 1234]; + assertEquals(4, angular.Array.filter(items, "").length); + assertEquals(4, angular.Array.filter(items, undefined).length); + + assertEquals(1, angular.Array.filter(items, 'iSk').length); + assertEquals("MIsKO", angular.Array.filter(items, 'isk')[0]); + + assertEquals(1, angular.Array.filter(items, 'yam').length); + assertEquals(items[1], angular.Array.filter(items, 'yam')[0]); + + assertEquals(1, angular.Array.filter(items, 'da').length); + assertEquals(items[2], angular.Array.filter(items, 'da')[0]); + + assertEquals(1, angular.Array.filter(items, '34').length); + assertEquals(1234, angular.Array.filter(items, '34')[0]); + + assertEquals(0, angular.Array.filter(items, "I don't exist").length); +}; + +ApiTest.prototype.testShouldNotFilterOnSystemData = function() { + assertEquals("", "".charAt(0)); // assumption + var items = [{$name:"misko"}]; + assertEquals(0, angular.Array.filter(items, "misko").length); +}; + +ApiTest.prototype.testFilterOnSpecificProperty = function() { + var items = [{ignore:"a", name:"a"}, {ignore:"a", name:"abc"}]; + assertEquals(2, angular.Array.filter(items, {}).length); + + assertEquals(2, angular.Array.filter(items, {name:'a'}).length); + + assertEquals(1, angular.Array.filter(items, {name:'b'}).length); + assertEquals("abc", angular.Array.filter(items, {name:'b'})[0].name); +}; + +ApiTest.prototype.testFilterOnFunction = function() { + var items = [{name:"a"}, {name:"abc", done:true}]; + assertEquals(1, angular.Array.filter(items, function(i){return i.done;}).length); +}; + +ApiTest.prototype.testFilterIsAndFunction = function() { + var items = [{first:"misko", last:"hevery"}, + {first:"adam", last:"abrons"}]; + + assertEquals(2, angular.Array.filter(items, {first:'', last:''}).length); + assertEquals(1, angular.Array.filter(items, {first:'', last:'hevery'}).length); + assertEquals(0, angular.Array.filter(items, {first:'adam', last:'hevery'}).length); + assertEquals(1, angular.Array.filter(items, {first:'misko', last:'hevery'}).length); + assertEquals(items[0], angular.Array.filter(items, {first:'misko', last:'hevery'})[0]); +}; + +ApiTest.prototype.testFilterNot = function() { + var items = ["misko", "adam"]; + + assertEquals(1, angular.Array.filter(items, '!isk').length); + assertEquals(items[1], angular.Array.filter(items, '!isk')[0]); +}; + +ApiTest.prototype.testAdd = function() { + var add = angular.Array.add; + assertJsonEquals([{}, "a"], add(add([]),"a")); +}; + +ApiTest.prototype.testCount = function() { + var array = [{name:'a'},{name:'b'},{name:''}]; + var obj = {}; + + assertEquals(3, angular.Array.count(array)); + assertEquals(2, angular.Array.count(array, 'name')); + assertEquals(1, angular.Array.count(array, 'name=="a"')); +}; + +ApiTest.prototype.testFind = function() { + var array = [{name:'a'},{name:'b'},{name:''}]; + var obj = {}; + + assertEquals(undefined, angular.Array.find(array, 'false')); + assertEquals('default', angular.Array.find(array, 'false', 'default')); + assertEquals('a', angular.Array.find(array, 'name == "a"').name); + assertEquals('', angular.Array.find(array, 'name == ""').name); +}; + +ApiTest.prototype.testItShouldSortArray = function() { + assertEquals([2,15], angular.Array.orderBy([15,2])); + assertEquals(["a","B", "c"], angular.Array.orderBy(["c","B", "a"])); + assertEquals([15,"2"], angular.Array.orderBy([15,"2"])); + assertEquals(["15","2"], angular.Array.orderBy(["15","2"])); + assertJsonEquals([{a:2},{a:15}], angular.Array.orderBy([{a:15},{a:2}], 'a')); + assertJsonEquals([{a:2},{a:15}], angular.Array.orderBy([{a:15},{a:2}], 'a', "F")); +}; + +ApiTest.prototype.testItShouldSortArrayInReverse = function() { + assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', true)); + assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', "T")); + assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', "reverse")); +}; + +ApiTest.prototype.testItShouldSortArrayByPredicate = function() { + assertJsonEquals([{a:2, b:1},{a:15, b:1}], + angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['a', 'b'])); + assertJsonEquals([{a:2, b:1},{a:15, b:1}], + angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['b', 'a'])); + assertJsonEquals([{a:15, b:1},{a:2, b:1}], + angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['+b', '-a'])); +}; + +ApiTest.prototype.testQuoteString = function(){ + assertEquals(angular.String.quote('a'), '"a"'); + assertEquals(angular.String.quote('\\'), '"\\\\"'); + assertEquals(angular.String.quote("'a'"), '"\'a\'"'); + assertEquals(angular.String.quote('"a"'), '"\\"a\\""'); + assertEquals(angular.String.quote('\n\f\r\t'), '"\\n\\f\\r\\t"'); +}; + +ApiTest.prototype.testQuoteStringBug = function(){ + assertEquals(angular.String.quote('"7\\\\\\\"7"', "7\\\"7")); +}; + +ApiTest.prototype.testQuoteUnicode = function(){ + assertEquals('"abc\\u00a0def"', angular.String.quoteUnicode('abc\u00A0def')); +}; + +ApiTest.prototype.testMerge = function() { + var array = [{name:"misko"}]; + angular.Array.merge(array, 0, {name:"", email:"email1"}); + angular.Array.merge(array, 1, {name:"adam", email:"email2"}); + assertJsonEquals([{"email":"email1","name":"misko"},{"email":"email2","name":"adam"}], array); +}; + +ApiTest.prototype.testOrderByToggle = function() { + var orderByToggle = angular.Array.orderByToggle; + var predicate = []; + assertEquals(['+a'], orderByToggle(predicate, 'a')); + assertEquals(['-a'], orderByToggle(predicate, 'a')); + + assertEquals(['-a', '-b'], orderByToggle(['-b', 'a'], 'a')); +}; + +ApiTest.prototype.testOrderByToggle = function() { + var orderByDirection = angular.Array.orderByDirection; + assertEquals("", orderByDirection(['+a','b'], 'x')); + assertEquals("", orderByDirection(['+a','b'], 'b')); + assertEquals('ng-ascend', orderByDirection(['a','b'], 'a')); + assertEquals('ng-ascend', orderByDirection(['+a','b'], 'a')); + assertEquals('ng-descend', orderByDirection(['-a','b'], 'a')); + assertEquals('up', orderByDirection(['+a','b'], 'a', 'up', 'down')); + assertEquals('down', orderByDirection(['-a','b'], 'a', 'up', 'down')); +}; + +ApiTest.prototype.testDateToUTC = function(){ + var date = new Date("Sep 10 2003 13:02:03 GMT"); + assertEquals("date", angular.Object.typeOf(date)); + assertEquals("2003-09-10T13:02:03Z", angular.Date.toString(date)); +}; + +ApiTest.prototype.testStringFromUTC = function(){ + var date = angular.String.toDate("2003-09-10T13:02:03Z"); + assertEquals("date", angular.Object.typeOf(date)); + assertEquals("2003-09-10T13:02:03Z", angular.Date.toString(date)); + assertEquals("str", angular.String.toDate("str")); +}; diff --git a/test/Base64Test.js b/test/Base64Test.js new file mode 100644 index 00000000..a9353186 --- /dev/null +++ b/test/Base64Test.js @@ -0,0 +1,5 @@ +Base64Test = TestCase('Base64Test'); + +Base64Test.prototype.testEncodeDecode = function(){ + assertEquals(Base64.decode(Base64.encode('hello')), 'hello'); +}; 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()); +}; diff --git a/test/ConsoleTest.js b/test/ConsoleTest.js new file mode 100644 index 00000000..56e223bd --- /dev/null +++ b/test/ConsoleTest.js @@ -0,0 +1,13 @@ +ConsoleTest = TestCase('ConsoleTest'); + +ConsoleTest.prototype.testConsoleWrite = function(){ + var consoleNode = $("<div></div>")[0]; + nglr.consoleNode = consoleNode; + nglr.consoleLog("error", ["Hello", "world"]); + assertEquals($(consoleNode)[0].nodeName, 'DIV'); + assertEquals($(consoleNode).text(), 'Hello world'); + assertEquals($('div', consoleNode)[0].className, 'error'); + nglr.consoleLog("error",["Bye"]); + assertEquals($(consoleNode).text(), 'Hello worldBye'); + nglr.consoleNode = null; +};
\ No newline at end of file diff --git a/test/ControlBarTest.js b/test/ControlBarTest.js new file mode 100644 index 00000000..c914c8ff --- /dev/null +++ b/test/ControlBarTest.js @@ -0,0 +1,2 @@ +ControlBarTest = TestCase("ControlBarTest"); + diff --git a/test/DataStoreTest.js b/test/DataStoreTest.js new file mode 100644 index 00000000..9fe6c3df --- /dev/null +++ b/test/DataStoreTest.js @@ -0,0 +1,617 @@ +DataStoreTest = TestCase('DataStoreTest'); + +DataStoreTest.prototype.testSavePostsToServer = function(){ + expectAsserts(10); + var response; + var post = function(data, callback){ + var method = data[0][0]; + var posted = data[0][2]; + assertEquals("POST", method); + assertEquals("abc", posted.$entity); + assertEquals("123", posted.$id); + assertEquals("1", posted.$version); + assertFalse('function' == typeof posted.save); + response = nglr.fromJson(nglr.toJson(posted)); + response.$entity = "abc"; + response.$id = "123"; + response.$version = "2"; + callback(200, [response]); + }; + var model; + var datastore = new nglr.DataStore(post); + model = datastore.entity('abc', {name: "value"})(); + model.$id = "123"; + model.$version = "1"; + + datastore.save(model, function(obj){ + assertTrue(obj === model); + assertEquals(obj.$entity, "abc"); + assertEquals(obj.$id, "123"); + assertEquals(obj.$version, "2"); + assertEquals(obj.name, "value"); + obj.after = true; + }); + datastore.flush(); +}; + +DataStoreTest.prototype.testLoadGetsFromServer = function(){ + expectAsserts(12); + var post = function(data, callback){ + var method = data[0][0]; + var path = data[0][1]; + assertEquals("GET", method); + assertEquals("abc/1", path); + response = [{$entity:'abc', $id:'1', $version:'2', key:"value"}]; + callback(200, response); + }; + var datastore = new nglr.DataStore(post); + + var model = datastore.entity("abc", {merge:true})(); + assertEquals(datastore.load(model, '1', function(obj){ + assertEquals(obj.$entity, "abc"); + assertEquals(obj.$id, "1"); + assertEquals(obj.$version, "2"); + assertEquals(obj.key, "value"); + }), model); + datastore.flush(); + assertEquals(model.$entity, "abc"); + assertEquals(model.$id, "1"); + assertEquals(model.$version, "2"); + assertEquals(model.key, "value"); + assertEquals(model.merge, true); +}; + +DataStoreTest.prototype.testRemove = function(){ + expectAsserts(8); + var response; + var post = function(data, callback){ + var method = data[0][0]; + var posted = data[0][2]; + assertEquals("DELETE", method); + assertEquals("abc", posted.$entity); + assertEquals("123", posted.$id); + assertEquals("1", posted.$version); + assertFalse('function' == typeof posted.save); + response = nglr.fromJson(nglr.toJson(posted)); + response.$entity = "abc"; + response.$id = "123"; + response.$version = "2"; + callback(200, [response]); + }; + var model; + var datastore = new nglr.DataStore(post); + model = datastore.entity('abc', {name: "value"})(); + model.$id = "123"; + model.$version = "1"; + + datastore.remove(model, function(obj){ + assertEquals(obj.$id, "123"); + assertEquals(obj.$version, "2"); + assertEquals(obj.name, "value"); + obj.after = true; + }); + datastore.flush(); + +}; + + +DataStoreTest.prototype.test401ResponseDoesNotCallCallback = function(){ + expectAsserts(1); + var post = function(data, callback) { + callback(200, {$status_code: 401}); + }; + + var datastore = new nglr.DataStore(post, {login:function(){ + assertTrue(true); + }}); + + var onLoadAll = function(){ + assertTrue(false, "onLoadAll should not be called when response is status 401"); + }; + datastore.bulkRequest.push({}); + datastore.flush(); + datastore.loadAll({type: "A"}, onLoadAll); +}; + +DataStoreTest.prototype.test403ResponseDoesNotCallCallback = function(){ + expectAsserts(1); + var post = function(data, callback) { + callback(200, [{$status_code: 403}]); + }; + + var datastore = new nglr.DataStore(post, {notAuthorized:function(){ + assertTrue(true); + }}); + + var onLoadAll = function(){ + assertTrue(false, "onLoadAll should not be called when response is status 403"); + }; + datastore.bulkRequest.push({}); + datastore.flush(); + datastore.loadAll({type: "A"}, onLoadAll); +}; + +DataStoreTest.prototype.testLoadCalledWithoutIdShouldBeNoop = function(){ + expectAsserts(2); + var post = function(url, callback){ + assertTrue(false); + }; + var datastore = new nglr.DataStore(post); + var model = datastore.entity("abc")(); + assertEquals(datastore.load(model, undefined), model); + assertEquals(model.$entity, "abc"); +}; + +DataStoreTest.prototype.testEntityFactory = function(){ + var ds = new nglr.DataStore(); + var Recipe = ds.entity("Recipe", {a:1, b:2}); + assertEquals(Recipe.title, "Recipe"); + assertEquals(Recipe.defaults.a, 1); + assertEquals(Recipe.defaults.b, 2); + + var recipe = Recipe(); + assertEquals(recipe.$entity, "Recipe"); + assertEquals(recipe.a, 1); + assertEquals(recipe.b, 2); + + recipe = new Recipe(); + assertEquals(recipe.$entity, "Recipe"); + assertEquals(recipe.a, 1); + assertEquals(recipe.b, 2); +}; + +DataStoreTest.prototype.testEntityFactoryNoDefaults = function(){ + var ds = new nglr.DataStore(); + var Recipe = ds.entity("Recipe"); + assertEquals(Recipe.title, "Recipe"); + + recipe = new Recipe(); + assertEquals(recipe.$entity, "Recipe"); +}; + +DataStoreTest.prototype.testEntityFactoryWithInitialValues = function(){ + var ds = new nglr.DataStore(); + var Recipe = ds.entity("Recipe"); + + var recipe = Recipe({name: "name"}); + assertEquals("name", recipe.name); +}; + +DataStoreTest.prototype.testEntityLoad = function(){ + var ds = new nglr.DataStore(); + var Recipe = ds.entity("Recipe", {a:1, b:2}); + ds.load = function(instance, id, callback){ + callback.apply(instance); + return instance; + }; + var instance = null; + var recipe2 = Recipe.load("ID", function(){ + instance = this; + }); + assertTrue(recipe2 === instance); +}; + +DataStoreTest.prototype.testSaveScope = function(){ + var ds = new nglr.DataStore(); + var log = ""; + var Person = ds.entity("Person"); + var person1 = Person({name:"A", $entity:"Person", $id:"1", $version:"1"}, ds); + person1.$$anchor = "A"; + var person2 = Person({name:"B", $entity:"Person", $id:"2", $version:"2"}, ds); + person2.$$anchor = "B"; + var anchor = {}; + ds.anchor = anchor; + ds._jsonRequest = function(request, callback){ + log += "save(" + request[2].$id + ");"; + callback({$id:request[2].$id}); + }; + ds.saveScope({person1:person1, person2:person2, + ignoreMe:{name: "ignore", save:function(callback){callback();}}}, function(){ + log += "done();"; + }); + assertEquals("save(1);save(2);done();", log); + assertEquals(1, anchor.A); + assertEquals(2, anchor.B); +}; + +DataStoreTest.prototype.testEntityLoadAllRows = function(){ + var ds = new nglr.DataStore(); + var Recipe = ds.entity("Recipe"); + var list = []; + ds.loadAll = function(entity, callback){ + assertTrue(Recipe === entity); + callback.apply(list); + return list; + }; + var items = Recipe.all(function(){ + assertTrue(list === this); + }); + assertTrue(items === list); +}; + +DataStoreTest.prototype.testLoadAll = function(){ + expectAsserts(8); + var post = function(data, callback){ + assertEquals("GET", data[0][0]); + assertEquals("A", data[0][1]); + callback(200, [[{$entity:'A', $id:'1'},{$entity:'A', $id:'2'}]]); + }; + var datastore = new nglr.DataStore(post); + var list = datastore.entity("A").all(function(){ + assertTrue(true); + }); + datastore.flush(); + assertEquals(list.length, 2); + assertEquals(list[0].$entity, "A"); + assertEquals(list[0].$id, "1"); + assertEquals(list[1].$entity, "A"); + assertEquals(list[1].$id, "2"); +}; + +DataStoreTest.prototype.testQuery = function(){ + expectAsserts(5); + var post = function(data, callback) { + assertEquals("GET", data[0][0]); + assertEquals("Employee/managerId=123abc", data[0][1]); + callback(200, [[{$entity:"Employee", $id: "456", managerId: "123ABC"}]]); + + }; + var datastore = new nglr.DataStore(post); + var Employee = datastore.entity("Employee"); + var list = Employee.query('managerId', "123abc", function(){ + assertTrue(true); + }); + datastore.flush(); + assertJsonEquals([[{$entity:"Employee", $id: "456", managerId: "123ABC"}]], datastore._cache.$collections); + assertEquals(list[0].$id, "456"); +}; + +DataStoreTest.prototype.testLoadingDocumentRefreshesExistingArrays = function() { + expectAsserts(12); + var post; + var datastore = new nglr.DataStore(function(r, c){post(r,c);}); + var Book = datastore.entity('Book'); + post = function(req, callback) { + callback(200, [[{$id:1, $entity:"Book", name:"Moby"}, + {$id:2, $entity:"Book", name:"Dick"}]]); + }; + var allBooks = Book.all(); + datastore.flush(); + var queryBooks = Book.query("a", "b"); + datastore.flush(); + assertEquals("Moby", allBooks[0].name); + assertEquals("Dick", allBooks[1].name); + assertEquals("Moby", queryBooks[0].name); + assertEquals("Dick", queryBooks[1].name); + + post = function(req, callback) { + assertEquals('[["GET","Book/1"]]', nglr.toJson(req)); + callback(200, [{$id:1, $entity:"Book", name:"Moby Dick"}]); + }; + var book = Book.load(1); + datastore.flush(); + assertEquals("Moby Dick", book.name); + assertEquals("Moby Dick", allBooks[0].name); + assertEquals("Moby Dick", queryBooks[0].name); + + post = function(req, callback) { + assertEquals('POST', req[0][0]); + callback(200, [{$id:1, $entity:"Book", name:"The Big Fish"}]); + }; + book.$save(); + datastore.flush(); + assertEquals("The Big Fish", book.name); + assertEquals("The Big Fish", allBooks[0].name); + assertEquals("The Big Fish", queryBooks[0].name); +}; + +DataStoreTest.prototype.testEntityProperties = function() { + expectAsserts(2); + var datastore = new nglr.DataStore(); + var callback = {}; + + datastore._jsonRequest = function(request, callbackFn) { + assertJsonEquals(["GET", "Cheese/$properties"], request); + assertEquals(callback, callbackFn); + }; + + var Cheese = datastore.entity("Cheese"); + Cheese.properties(callback); + +}; + +DataStoreTest.prototype.testLoadInstanceIsNotFromCache = function() { + var post; + var datastore = new nglr.DataStore(function(r, c){post(r,c);}); + var Book = datastore.entity('Book'); + + post = function(req, callback) { + assertEquals('[["GET","Book/1"]]', nglr.toJson(req)); + callback(200, [{$id:1, $entity:"Book", name:"Moby Dick"}]); + }; + var book = Book.load(1); + datastore.flush(); + assertEquals("Moby Dick", book.name); + assertFalse(book === datastore._cache['Book/1']); +}; + +DataStoreTest.prototype.testLoadStarsIsNewDocument = function() { + var datastore = new nglr.DataStore(); + var Book = datastore.entity('Book'); + var book = Book.load('*'); + assertEquals('Book', book.$entity); +}; + +DataStoreTest.prototype.testUndefinedEntityReturnsNullValueObject = function() { + var datastore = new nglr.DataStore(); + var Entity = datastore.entity(undefined); + var all = Entity.all(); + assertEquals(0, all.length); +}; + +DataStoreTest.prototype.testFetchEntities = function(){ + expectAsserts(6); + var post = function(data, callback){ + assertJsonEquals(["GET", "$entities"], data[0]); + callback(200, [{A:0, B:0}]); + }; + var datastore = new nglr.DataStore(post); + var entities = datastore.entities(function(){ + assertTrue(true); + }); + datastore.flush(); + assertJsonEquals([], datastore.bulkRequest); + assertEquals(2, entities.length); + assertEquals("A", entities[0].title); + assertEquals("B", entities[1].title); +}; + +DataStoreTest.prototype.testItShouldMigrateSchema = function() { + var datastore = new nglr.DataStore(); + var Entity = datastore.entity("Entity", {a:[], user:{name:"Misko", email:""}}); + var doc = Entity().$loadFrom({b:'abc', user:{email:"misko@hevery.com"}}); + assertFalse( + nglr.toJson({a:[], b:'abc', user:{name:"Misko", email:"misko@hevery.com"}}) == + nglr.toJson(doc)); + doc.$migrate(); + assertEquals( + nglr.toJson({a:[], b:'abc', user:{name:"Misko", email:"misko@hevery.com"}}), + nglr.toJson(doc)); +}; + +DataStoreTest.prototype.testItShouldCollectRequestsForBulk = function() { + var ds = new nglr.DataStore(); + var Book = ds.entity("Book"); + var Library = ds.entity("Library"); + Book.all(); + Library.load("123"); + assertEquals(2, ds.bulkRequest.length); + assertJsonEquals(["GET", "Book"], ds.bulkRequest[0]); + assertJsonEquals(["GET", "Library/123"], ds.bulkRequest[1]); +}; + +DataStoreTest.prototype.testEmptyFlushShouldDoNothing = function () { + var ds = new nglr.DataStore(function(){ + fail("expecting noop"); + }); + ds.flush(); +}; + +DataStoreTest.prototype.testFlushShouldCallAllCallbacks = function() { + var log = ""; + function post(request, callback){ + log += 'BulkRequest:' + nglr.toJson(request) + ';'; + callback(200, [[{$id:'ABC'}], {$id:'XYZ'}]); + } + var ds = new nglr.DataStore(post); + var Book = ds.entity("Book"); + var Library = ds.entity("Library"); + Book.all(function(instance){ + log += nglr.toJson(instance) + ';'; + }); + Library.load("123", function(instance){ + log += nglr.toJson(instance) + ';'; + }); + assertEquals("", log); + ds.flush(); + assertJsonEquals([], ds.bulkRequest); + assertEquals('BulkRequest:[["GET","Book"],["GET","Library/123"]];[{"$id":"ABC"}];{"$id":"XYZ"};', log); +}; + +DataStoreTest.prototype.testSaveOnNotLoggedInRetriesAfterLoggin = function(){ + var log = ""; + var book; + var ds = new nglr.DataStore(null, {login:function(c){c();}}); + ds.post = function (request, callback){ + assertJsonEquals([["POST", "", book]], request); + ds.post = function(request, callback){ + assertJsonEquals([["POST", "", book]], request); + ds.post = function(){fail("too much recursion");}; + callback(200, [{saved:"ok"}]); + }; + callback(200, {$status_code:401}); + }; + book = ds.entity("Book")({name:"misko"}); + book.$save(); + ds.flush(); + assertJsonEquals({saved:"ok"}, book); +}; + +DataStoreTest.prototype.testItShouldRemoveItemFromCollectionWhenDeleted = function() { + expectAsserts(6); + var ds = new nglr.DataStore(); + ds.post = function(request, callback){ + assertJsonEquals([["GET", "Book"]], request); + callback(200, [[{name:"Moby Dick", $id:123, $entity:'Book'}]]); + }; + var Book = ds.entity("Book"); + var books = Book.all(); + ds.flush(); + assertJsonEquals([[{name:"Moby Dick", $id:123, $entity:'Book'}]], ds._cache.$collections); + assertDefined(ds._cache['Book/123']); + var book = Book({$id:123}); + ds.post = function(request, callback){ + assertJsonEquals([["DELETE", "", book]], request); + callback(200, [book]); + }; + ds.remove(book); + ds.flush(); + assertUndefined(ds._cache['Book/123']); + assertJsonEquals([[]],ds._cache.$collections); +}; + +DataStoreTest.prototype.testItShouldAddToAll = function() { + expectAsserts(8); + var ds = new nglr.DataStore(); + ds.post = function(request, callback){ + assertJsonEquals([["GET", "Book"]], request); + callback(200, [[]]); + }; + var Book = ds.entity("Book"); + var books = Book.all(); + assertEquals(0, books.length); + ds.flush(); + var moby = Book({name:'moby'}); + moby.$save(); + ds.post = function(request, callback){ + assertJsonEquals([["POST", "", moby]], request); + moby.$id = '123'; + callback(200, [moby]); + }; + ds.flush(); + assertEquals(1, books.length); + assertEquals(moby, books[0]); + + moby.$save(); + ds.flush(); + assertEquals(1, books.length); + assertEquals(moby, books[0]); +}; + +DataStoreTest.prototype.testItShouldReturnCreatedDocumentCountByUser = function(){ + expectAsserts(2); + var datastore = new nglr.DataStore( + function(request, callback){ + assertJsonEquals([["GET", "$users"]], request); + callback(200, [{misko:1, adam:1}]); + }); + var users = datastore.documentCountsByUser(); + assertJsonEquals({misko:1, adam:1}, users); +}; + + +DataStoreTest.prototype.testItShouldReturnDocumentIdsForUeserByEntity = function(){ + expectAsserts(2); + var datastore = new nglr.DataStore( + function(request, callback){ + assertJsonEquals([["GET", "$users/misko@hevery.com"]], request); + callback(200, [{Book:["1"], Library:["2"]}]); + }); + var users = datastore.userDocumentIdsByEntity("misko@hevery.com"); + assertJsonEquals({Book:["1"], Library:["2"]}, users); +}; + +DataStoreTest.prototype.testItShouldReturnNewInstanceOn404 = function(){ + expectAsserts(7); + var log = ""; + var datastore = new nglr.DataStore( + function(request, callback){ + assertJsonEquals([["GET", "User/misko"]], request); + callback(200, [{$status_code:404}]); + }); + var User = datastore.entity("User", {admin:false}); + var user = User.loadOrCreate('misko', function(i){log+="cb "+i.$id+";";}); + datastore.flush(); + assertEquals("misko", user.$id); + assertEquals("User", user.$entity); + assertEquals(false, user.admin); + assertEquals("undefined", typeof user.$secret); + assertEquals("undefined", typeof user.$version); + assertEquals("cb misko;", log); +}; + +DataStoreTest.prototype.testItShouldReturnNewInstanceOn404 = function(){ + var log = ""; + var datastore = new nglr.DataStore( + function(request, callback){ + assertJsonEquals([["GET", "User/misko"],["GET", "User/adam"]], request); + callback(200, [{$id:'misko'},{$id:'adam'}]); + }); + var User = datastore.entity("User"); + var users = User.loadMany(['misko', 'adam'], function(i){log+="cb "+nglr.toJson(i)+";";}); + datastore.flush(); + assertEquals("misko", users[0].$id); + assertEquals("adam", users[1].$id); + assertEquals('cb [{"$id":"misko"},{"$id":"adam"}];', log); +}; + +DataStoreTest.prototype.testItShouldCreateJoinAndQuery = function() { + var datastore = new nglr.DataStore(); + var Invoice = datastore.entity("Invoice"); + var Customer = datastore.entity("Customer"); + var InvoiceWithCustomer = datastore.join({ + invoice:{join:Invoice}, + customer:{join:Customer, on:"invoice.customer"} + }); + var invoiceWithCustomer = InvoiceWithCustomer.query("invoice.month", 1); + assertEquals([], invoiceWithCustomer); + assertJsonEquals([["GET", "Invoice/month=1"]], datastore.bulkRequest); + var request = datastore.bulkRequest.shift(); + request.$$callback([{$id:1, customer:1},{$id:2, customer:1},{$id:3, customer:3}]); + assertJsonEquals([["GET","Customer/1"],["GET","Customer/3"]], datastore.bulkRequest); + datastore.bulkRequest.shift().$$callback({$id:1}); + datastore.bulkRequest.shift().$$callback({$id:3}); + assertJsonEquals([ + {invoice:{$id:1,customer:1},customer:{$id:1}}, + {invoice:{$id:2,customer:1},customer:{$id:1}}, + {invoice:{$id:3,customer:3},customer:{$id:3}}], invoiceWithCustomer); +}; + +DataStoreTest.prototype.testItShouldThrowIfMoreThanOneEntityIsPrimary = function() { + var datastore = new nglr.DataStore(); + var Invoice = datastore.entity("Invoice"); + var Customer = datastore.entity("Customer"); + assertThrows("Exactly one entity needs to be primary.", function(){ + datastore.join({ + invoice:{join:Invoice}, + customer:{join:Customer} + }); + }); +}; + +DataStoreTest.prototype.testItShouldThrowIfLoopInReferences = function() { + var datastore = new nglr.DataStore(); + var Invoice = datastore.entity("Invoice"); + var Customer = datastore.entity("Customer"); + assertThrows("Infinite loop in join: invoice -> customer", function(){ + datastore.join({ + invoice:{join:Invoice, on:"customer.invoice"}, + customer:{join:Customer, on:"invoice.customer"} + }); + }); +}; + +DataStoreTest.prototype.testItShouldThrowIfReferenceToNonExistantJoin = function() { + var datastore = new nglr.DataStore(); + var Invoice = datastore.entity("Invoice"); + var Customer = datastore.entity("Customer"); + assertThrows("Named entity 'x' is undefined.", function(){ + datastore.join({ + invoice:{join:Invoice, on:"x.invoice"}, + customer:{join:Customer, on:"invoice.customer"} + }); + }); +}; + +DataStoreTest.prototype.testItShouldThrowIfQueryOnNonPrimary = function() { + var datastore = new nglr.DataStore(); + var Invoice = datastore.entity("Invoice"); + var Customer = datastore.entity("Customer"); + var InvoiceWithCustomer = datastore.join({ + invoice:{join:Invoice}, + customer:{join:Customer, on:"invoice.customer"} + }); + assertThrows("Named entity 'customer' is not a primary entity.", function(){ + InvoiceWithCustomer.query("customer.month", 1); + }); +}; diff --git a/test/EntityDeclarationTest.js b/test/EntityDeclarationTest.js new file mode 100644 index 00000000..5cab90f4 --- /dev/null +++ b/test/EntityDeclarationTest.js @@ -0,0 +1,46 @@ +EntityDeclarationTest = TestCase('EntityDeclarationTest'); + +EntityDeclarationTest.prototype.testEntityTypeOnly = function(){ + expectAsserts(2); + var scope = new nglr.Scope({$datastore:{entity:function(name){ + assertEquals("Person", name); + }}}); + var init = scope.entity("Person"); + assertEquals("", init); +}; + +EntityDeclarationTest.prototype.testWithDefaults = function(){ + expectAsserts(4); + var scope = new nglr.Scope({$datastore:{entity:function(name, init){ + assertEquals("Person", name); + assertEquals("=a:", init.a); + assertEquals(0, init.b.length); + }}}); + var init = scope.entity('Person:{a:"=a:", b:[]}'); + assertEquals("", init); +}; + +EntityDeclarationTest.prototype.testWithName = function(){ + expectAsserts(2); + var scope = new nglr.Scope({$datastore:{entity:function(name, init){ + assertEquals("Person", name); + return function (){ return {}; }; + }}}); + var init = scope.entity('friend=Person'); + assertEquals("$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};", init); +}; + +EntityDeclarationTest.prototype.testMultipleEntities = function(){ + expectAsserts(3); + var expect = ['Person', 'Book']; + var i=0; + var scope = new nglr.Scope({$datastore:{entity:function(name, init){ + assertEquals(expect[i], name); + i++; + return function (){ return {}; }; + }}}); + var init = scope.entity('friend=Person;book=Book;'); + assertEquals("$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};" + + "$anchor.book:{book=Book.load($anchor.book);book.$$anchor=\"book\";};", + init); +}; diff --git a/test/FileControllerTest.js b/test/FileControllerTest.js new file mode 100644 index 00000000..ca5925e4 --- /dev/null +++ b/test/FileControllerTest.js @@ -0,0 +1,98 @@ +FileControllerTest = TestCase('FileControllerTest'); + +FileControllerTest.prototype.testOnSelectUpdateView = function(){ + var view = jQuery('<span><a/><span/></span>'); + var swf = {}; + var controller = new nglr.FileController(view, null, swf); + swf.uploadFile = function(path){}; + controller._on_select('A', 9, '9 bytes'); + assertEquals(view.find('a').text(), "A"); + assertEquals(view.find('span').text(), "9 bytes"); +}; + +FileControllerTest.prototype.testUpdateModelView = function(){ + var view = nglr.FileController.template(''); + var input = $('<input name="value.input">'); + var controller; + var scope = new nglr.Scope({value:{}, $binder:{updateView:function(){ + controller.updateView(scope); + }}}); + view.data('scope', scope); + controller = new nglr.FileController(view, 'value.input', null, "http://server_base"); + var value = '{"text":"A", "size":123, "id":"890"}'; + controller._on_uploadCompleteData(value); + controller.updateView(scope); + assertEquals(scope.get('value.input.text'), 'A'); + assertEquals(scope.get('value.input.size'), 123); + assertEquals(scope.get('value.input.id'), '890'); + assertEquals(scope.get('value.input.url'), 'http://server_base/_attachments/890/A'); + assertEquals(view.find('a').text(), "A"); + assertEquals(view.find('a').attr('href'), "http://server_base/_attachments/890/A"); + assertEquals(view.find('span').text(), "123 bytes"); +}; + +FileControllerTest.prototype.testFileUpload = function(){ + expectAsserts(1); + var swf = {}; + var controller = new nglr.FileController(null, null, swf, "http://server_base"); + swf.uploadFile = function(path){ + assertEquals("http://server_base/_attachments", path); + }; + controller.name = "Name"; + controller.upload(); +}; + +FileControllerTest.prototype.testFileUploadNoFileIsNoop = function(){ + expectAsserts(0); + var swf = {uploadFile:function(path){ + fail(); + }}; + var controller = new nglr.FileController(null, swf); + controller.upload("basePath", null); +}; + +FileControllerTest.prototype.testRemoveAttachment = function(){ + var doc = nglr.FileController.template(); + var input = $('<input name="file">'); + var scope = new nglr.Scope(); + input.data('scope', scope); + var controller = new nglr.FileController(doc, 'file', null, null); + controller.updateView(scope); + assertEquals(false, doc.find('input').attr('checked')); + + scope.set('file', {url:'url', size:123}); + controller.updateView(scope); + assertEquals(true, doc.find('input').attr('checked')); + + doc.find('input').attr('checked', false); + controller.updateModel(scope); + assertNull(scope.get('file')); + + doc.find('input').attr('checked', true); + controller.updateModel(scope); + assertEquals('url', scope.get('file.url')); + assertEquals(123, scope.get('file.size')); +}; + +FileControllerTest.prototype.testShouldEmptyOutOnUndefined = function () { + var view = nglr.FileController.template('hello'); + var controller = new nglr.FileController(view, 'abc', null, null); + + var scope = new nglr.Scope(); + scope.set('abc', {text: 'myname', url: 'myurl', size: 1234}); + + controller.updateView(scope); + assertEquals("myurl", view.find('a').attr('href')); + assertEquals("myname", view.find('a').text()); + assertEquals(true, view.find('input').is(':checked')); + assertEquals("1.2 KB", view.find('span').text()); + + scope.set('abc', undefined); + controller.updateView(scope); + assertEquals("myurl", view.find('a').attr('href')); + assertEquals("myname", view.find('a').text()); + assertEquals(false, view.find('input').is(':checked')); + assertEquals("1.2 KB", view.find('span').text()); +}; + + diff --git a/test/FiltersTest.js b/test/FiltersTest.js new file mode 100644 index 00000000..8943fdd4 --- /dev/null +++ b/test/FiltersTest.js @@ -0,0 +1,153 @@ +FiltersTest = TestCase('FiltersTest'); + +FiltersTest.prototype.testCurrency = function(){ + var html = $('<span/>'); + var context = {element:html[0]}; + var currency = nglr.bind(context, angular.filter.currency); + + assertEquals(currency(0), '$0.00'); + assertEquals(html.hasClass('ng-format-negative'), false); + assertEquals(currency(-999), '$-999.00'); + assertEquals(html.hasClass('ng-format-negative'), true); + assertEquals(currency(1234.5678), '$1,234.57'); + assertEquals(html.hasClass('ng-format-negative'), false); +}; + +FiltersTest.prototype.testFilterThisIsContext = function(){ + expectAsserts(2); + var scope = new nglr.Scope(); + nglr.Scope.expressionCache = {}; + var context = {element:123}; + angular.filter.testFn = function () { + assertEquals('Context not equal', this, context); + assertEquals('scope not equal', this.scope, scope); + }; + scope.eval("0|testFn", context); + delete angular.filter['testFn']; +}; + +FiltersTest.prototype.testNumberFormat = function(){ + var context = {jqElement:$('<span/>')}; + var number = nglr.bind(context, angular.filter.number); + + assertEquals('0', number(0, 0)); + assertEquals('0.00', number(0)); + assertEquals('-999.00', number(-999)); + assertEquals('1,234.57', number(1234.5678)); + assertEquals('', number(Number.NaN)); + assertEquals('1,234.57', number("1234.5678")); + assertEquals("", number(1/0)); +}; + +FiltersTest.prototype.testJson = function () { + assertEquals(nglr.toJson({a:"b"}, true), angular.filter.json({a:"b"})); +}; + +FiltersTest.prototype.testPackageTracking = function () { + var assert = function(title, trackingNo) { + var val = angular.filter.trackPackage(trackingNo, title); + assertNotNull("Did Not Match: " + trackingNo, val); + assertEquals(angular.filter.Meta.TAG, val.TAG); + assertEquals(title + ": " + nglr.trim(trackingNo), val.text); + assertNotNull(val.url); + assertEquals(nglr.trim(trackingNo), val.trackingNo); + assertEquals('<a href="' + val.url + '">' + val.text + '</a>', val.html); + }; + assert('UPS', ' 1Z 999 999 99 9999 999 9 '); + assert('UPS', '1ZW5w5220379084747'); + + assert('FedEx', '418822131061812'); + assert('FedEx', '9612019 5935 3267 2473 738'); + assert('FedEx', '9612019593532672473738'); + assert('FedEx', '235354667129449'); + assert('FedEx', '915368880571'); + assert('FedEx', '901712142390'); + assert('FedEx', '297391510063413'); + + assert('USPS', '9101 8052 1390 7402 4335 49'); + assert('USPS', '9101010521297963339560'); + assert('USPS', '9102901001301038667029'); + assert('USPS', '910 27974 4490 3000 8916 56'); + assert('USPS', '9102801438635051633253'); +}; + +FiltersTest.prototype.testLink = function() { + var assert = function(text, url, obj){ + var val = angular.filter.link(obj); + assertEquals(angular.filter.Meta.TAG, val.TAG); + assertEquals('<a href="' + url + '">' + text + '</a>', val.html); + }; + assert("url", "url", "url"); + assert("hello", "url", {text:"hello", url:"url"}); + assert("a@b.com", "mailto:a@b.com", "a@b.com"); +}; + +FiltersTest.prototype.testBytes = function(){ + var controller = new nglr.FileController(); + assertEquals(angular.filter.bytes(123), '123 bytes'); + assertEquals(angular.filter.bytes(1234), '1.2 KB'); + assertEquals(angular.filter.bytes(1234567), '1.1 MB'); +}; + +FiltersTest.prototype.testImage = function(){ + assertEquals(null, angular.filter.image()); + assertEquals(null, angular.filter.image({})); + assertEquals(null, angular.filter.image("")); + assertEquals('<img src="abc"/>', angular.filter.image({url:"abc"}).html); + assertEquals( + '<img src="abc" style="max-width: 10px; max-height: 10px;"/>', + angular.filter.image({url:"abc"}, 10).html); + assertEquals( + '<img src="abc" style="max-width: 10px; max-height: 20px;"/>', + angular.filter.image({url:"abc"}, 10, 20).html); +}; + +FiltersTest.prototype.testQRcode = function() { + assertEquals( + '<img width="200" height="200" src="http://chart.apis.google.com/chart?chl=Hello%20world&chs=200x200&cht=qr"/>', + angular.filter.qrcode('Hello world').html); + assertEquals( + '<img width="100" height="100" src="http://chart.apis.google.com/chart?chl=http%3A%2F%2Fserver%3Fa%26b%3Dc&chs=100x100&cht=qr"/>', + angular.filter.qrcode('http://server?a&b=c', 100).html); +}; + +FiltersTest.prototype.testLowercase = function() { + assertEquals('abc', angular.filter.lowercase('AbC')); + assertEquals(null, angular.filter.lowercase(null)); +}; + +FiltersTest.prototype.testUppercase = function() { + assertEquals('ABC', angular.filter.uppercase('AbC')); + assertEquals(null, angular.filter.uppercase(null)); +}; + +FiltersTest.prototype.testLineCount = function() { + assertEquals(1, angular.filter.linecount(null)); + assertEquals(1, angular.filter.linecount('')); + assertEquals(1, angular.filter.linecount('a')); + assertEquals(2, angular.filter.linecount('a\nb')); + assertEquals(3, angular.filter.linecount('a\nb\nc')); +}; + +FiltersTest.prototype.testIf = function() { + assertEquals('A', angular.filter['if']('A', true)); + assertEquals(undefined, angular.filter['if']('A', false)); +}; + +FiltersTest.prototype.testUnless = function() { + assertEquals('A', angular.filter.unless('A', false)); + assertEquals(undefined, angular.filter.unless('A', true)); +}; + +FiltersTest.prototype.testGoogleChartApiEncode = function() { + assertEquals( + '<img width="200" height="200" src="http://chart.apis.google.com/chart?chl=Hello world&chs=200x200&cht=qr"/>', + angular.filter.googleChartApi.encode({cht:"qr", chl:"Hello world"}).html); +}; + +FiltersTest.prototype.testHtml = function() { + assertEquals( + "a<b>c</b>d", + angular.filter.html("a<b>c</b>d").html); + assertTrue(angular.filter.html("a<b>c</b>d") instanceof angular.filter.Meta); +}; diff --git a/test/JsonTest.js b/test/JsonTest.js new file mode 100644 index 00000000..5c3644f5 --- /dev/null +++ b/test/JsonTest.js @@ -0,0 +1,69 @@ +JsonTest = TestCase("JsonTest"); + +JsonTest.prototype.testPrimitives = function () { + assertEquals("null", nglr.toJson(0/0)); + assertEquals("null", nglr.toJson(null)); + assertEquals("true", nglr.toJson(true)); + assertEquals("false", nglr.toJson(false)); + assertEquals("123.45", nglr.toJson(123.45)); + assertEquals('"abc"', nglr.toJson("abc")); + assertEquals('"a \\t \\n \\r b \\\\"', nglr.toJson("a \t \n \r b \\")); +}; + +JsonTest.prototype.testEscaping = function () { + assertEquals("\"7\\\\\\\"7\"", nglr.toJson("7\\\"7")); +}; + +JsonTest.prototype.testObjects = function () { + assertEquals('{"a":1,"b":2}', nglr.toJson({a:1,b:2})); + assertEquals('{"a":{"b":2}}', nglr.toJson({a:{b:2}})); + assertEquals('{"a":{"b":{"c":0}}}', nglr.toJson({a:{b:{c:0}}})); + assertEquals('{"a":{"b":null}}', nglr.toJson({a:{b:0/0}})); +}; + +JsonTest.prototype.testObjectPretty = function () { + assertEquals('{\n "a":1,\n "b":2}', nglr.toJson({a:1,b:2}, true)); + assertEquals('{\n "a":{\n "b":2}}', nglr.toJson({a:{b:2}}, true)); +}; + +JsonTest.prototype.testArray = function () { + assertEquals('[]', nglr.toJson([])); + assertEquals('[1,"b"]', nglr.toJson([1,"b"])); +}; + +JsonTest.prototype.testIgnoreFunctions = function () { + assertEquals('[null,1]', nglr.toJson([function(){},1])); + assertEquals('{}', nglr.toJson({a:function(){}})); +}; + +JsonTest.prototype.testParseNull = function () { + assertNull(nglr.fromJson("null")); +}; + +JsonTest.prototype.testParseBoolean = function () { + assertTrue(nglr.fromJson("true")); + assertFalse(nglr.fromJson("false")); +}; + +JsonTest.prototype.test$$isIgnored = function () { + assertEquals("{}", nglr.toJson({$$:0})); +}; + +JsonTest.prototype.testArrayWithEmptyItems = function () { + var a = []; + a[1] = "X"; + assertEquals('[null,"X"]', nglr.toJson(a)); +}; + +JsonTest.prototype.testItShouldEscapeUnicode = function () { + assertEquals(1, "\u00a0".length); + assertEquals(8, nglr.toJson("\u00a0").length); + assertEquals(1, nglr.fromJson(nglr.toJson("\u00a0")).length); +}; + +JsonTest.prototype.testItShouldUTCDates = function() { + var date = angular.String.toDate("2009-10-09T01:02:03Z"); + assertEquals('"2009-10-09T01:02:03Z"', nglr.toJson(date)); + assertEquals(date.getTime(), + nglr.fromJson('"2009-10-09T01:02:03Z"').getTime()); +}; diff --git a/test/LoaderTest.js b/test/LoaderTest.js new file mode 100644 index 00000000..91a804a5 --- /dev/null +++ b/test/LoaderTest.js @@ -0,0 +1,70 @@ +LoaderTest = TestCase('LoaderTest'); + +LoaderTest.prototype.testLoadCss = function(){ + if ($.browser.safari) return; + var head = jQuery('<head/>')[0]; + var loader = new nglr.Loader(document, head, {}); + var log = ''; + loader.config.server = 'http://'; + loader.loadCss('x'); + assertEquals($(head).find('link').attr('href'), 'http://x'); +}; + +LoaderTest.prototype.testDefaultDatabasePathFromSubdomain = function() { + var loader = new nglr.Loader(null, null, {server:"http://account.getangular.com", database:"database"}); + loader.computeConfiguration(); + assertEquals("database", loader.config.database); + + loader = new nglr.Loader(null, null, {server:"http://account.getangular.com"}); + loader.computeConfiguration(); + assertEquals("account", loader.config.database); + + loader = new nglr.Loader(null, null, {server:"https://account.getangular.com"}); + loader.computeConfiguration(); + assertEquals("account", loader.config.database); +}; + + + +UrlWatcherTest = TestCase('UrlWatcherTest'); + +UrlWatcherTest.prototype.testUrlWatcher = function () { + expectAsserts(2); + var location = {href:"http://server", hash:""}; + var watcher = new nglr.UrlWatcher(location); + watcher.delay = 1; + watcher.listener = function(url){ + assertEquals('http://getangular.test', url); + }; + watcher.setTimeout = function(fn, delay){ + assertEquals(1, delay); + location.href = "http://getangular.test"; + watcher.setTimeout = function(fn, delay) { + }; + fn(); + }; + watcher.watch(); +}; + +UrlWatcherTest.prototype.testItShouldFireOnUpdateEventWhenSpecialURLSet = function(){ + expectAsserts(2); + var location = {href:"http://server", hash:"#$iframe_notify=1234"}; + var watcher = new nglr.UrlWatcher(location); + nglr._iframe_notify_1234 = function () { + assertEquals("undefined", typeof nglr._iframe_notify_1234); + assertEquals("http://server2#", location.href); + }; + watcher.delay = 1; + watcher.expectedUrl = "http://server2"; + watcher.setTimeout = function(fn, delay){ + watcher.setTimeout = function(fn, delay) {}; + fn(); + }; + watcher.watch(); +}; + +FunctionTest = TestCase("FunctionTest"); + +FunctionTest.prototype.testEscapeHtml = function () { + assertEquals("<div>&amp;</div>", nglr.escapeHtml('<div>&</div>')); +};
\ No newline at end of file diff --git a/test/ModelTest.js b/test/ModelTest.js new file mode 100644 index 00000000..5d9119a1 --- /dev/null +++ b/test/ModelTest.js @@ -0,0 +1,84 @@ +ModelTest = TestCase('ModelTest'); + +ModelTest.prototype.testLoadSaveOperations = function(){ + var m1 = new nglr.DataStore().entity('A')(); + m1.a = 1; + + var m2 = {b:1}; + + m1.$loadFrom(m2); + + assertTrue(!m1.a); + assertEquals(m1.b, 1); +}; + +ModelTest.prototype.testLoadfromDoesNotClobberFunctions = function(){ + var m1 = new nglr.DataStore().entity('A')(); + m1.id = function(){return 'OK';}; + m1.$loadFrom({id:null}); + assertEquals(m1.id(), 'OK'); + + m1.b = 'OK'; + m1.$loadFrom({b:function(){}}); + assertEquals(m1.b, 'OK'); +}; + +ModelTest.prototype.testDataStoreDoesNotGetClobbered = function(){ + var ds = new nglr.DataStore(); + var m = ds.entity('A')(); + assertTrue(m.$$entity.datastore === ds); + m.$loadFrom({}); + assertTrue(m.$$entity.datastore === ds); +}; + +ModelTest.prototype.testManagedModelDelegatesMethodsToDataStore = function(){ + expectAsserts(7); + var datastore = new nglr.DataStore(); + var model = datastore.entity("A", {a:1})(); + var fn = {}; + datastore.save = function(instance, callback) { + assertTrue(model === instance); + assertTrue(callback === fn); + }; + datastore.remove = function(instance, callback) { + assertTrue(model === instance); + assertTrue(callback === fn); + }; + datastore.load = function(instance, id, callback) { + assertTrue(model === instance); + assertTrue(id === "123"); + assertTrue(callback === fn); + }; + model.$save(fn); + model.$delete(fn); + model.$loadById("123", fn); +}; + +ModelTest.prototype.testManagedModelCanBeForcedToFlush = function(){ + expectAsserts(6); + var datastore = new nglr.DataStore(); + var model = datastore.entity("A", {a:1})(); + + datastore.save = function(instance, callback) { + assertTrue(model === instance); + assertTrue(callback === undefined); + }; + datastore.remove = function(instance, callback) { + assertTrue(model === instance); + assertTrue(callback === undefined); + }; + datastore.flush = function(){ + assertTrue(true); + }; + model.$save(true); + model.$delete(true); +}; + + +ModelTest.prototype.testItShouldMakeDeepCopyOfInitialValues = function (){ + var initial = {a:[]}; + var entity = new nglr.DataStore().entity("A", initial); + var model = entity(); + model.a.push(1); + assertEquals(0, entity().a.length); +}; diff --git a/test/ParserTest.js b/test/ParserTest.js new file mode 100644 index 00000000..7fe8e6a4 --- /dev/null +++ b/test/ParserTest.js @@ -0,0 +1,462 @@ +LexerTest = TestCase('LexerTest'); + +LexerTest.prototype.testTokenizeAString = function(){ + var lexer = new nglr.Lexer("a.bc[22]+1.3|f:'a\\\'c':\"d\\\"e\""); + var tokens = lexer.parse(); + var i = 0; + assertEquals(tokens[i].index, 0); + assertEquals(tokens[i].text, 'a.bc'); + + i++; + assertEquals(tokens[i].index, 4); + assertEquals(tokens[i].text, '['); + + i++; + assertEquals(tokens[i].index, 5); + assertEquals(tokens[i].text, 22); + + i++; + assertEquals(tokens[i].index, 7); + assertEquals(tokens[i].text, ']'); + + i++; + assertEquals(tokens[i].index, 8); + assertEquals(tokens[i].text, '+'); + + i++; + assertEquals(tokens[i].index, 9); + assertEquals(tokens[i].text, 1.3); + + i++; + assertEquals(tokens[i].index, 12); + assertEquals(tokens[i].text, '|'); + + i++; + assertEquals(tokens[i].index, 13); + assertEquals(tokens[i].text, 'f'); + + i++; + assertEquals(tokens[i].index, 14); + assertEquals(tokens[i].text, ':'); + + i++; + assertEquals(tokens[i].index, 15); + assertEquals(tokens[i].text, "a'c"); + + i++; + assertEquals(tokens[i].index, 21); + assertEquals(tokens[i].text, ':'); + + i++; + assertEquals(tokens[i].index, 22); + assertEquals(tokens[i].text, 'd"e'); +}; + + +LexerTest.prototype.testTokenizeRegExp = function(){ + var lexer = new nglr.Lexer("/r 1/"); + var tokens = lexer.parse(); + var i = 0; + assertEquals(tokens[i].index, 0); + assertEquals(tokens[i].text, 'r 1'); + assertEquals("r 1".match(tokens[i].fn())[0], 'r 1'); +}; + +LexerTest.prototype.testQuotedString = function(){ + var str = "['\\'', \"\\\"\"]"; + var lexer = new nglr.Lexer(str); + var tokens = lexer.parse(); + + assertEquals(1, tokens[1].index); + assertEquals("'", tokens[1].text); + + assertEquals(7, tokens[3].index); + assertEquals('"', tokens[3].text); + +}; + +LexerTest.prototype.testQuotedStringEscape = function(){ + var str = '"\\"\\n\\f\\r\\t\\v\\u00A0"'; + var lexer = new nglr.Lexer(str); + var tokens = lexer.parse(); + + assertEquals('"\n\f\r\t\v\u00A0', tokens[0].text); +}; + +LexerTest.prototype.testTokenizeUnicode = function(){ + var lexer = new nglr.Lexer('"\\u00A0"'); + var tokens = lexer.parse(); + assertEquals(1, tokens.length); + assertEquals('\u00a0', tokens[0].text); +}; + +LexerTest.prototype.testTokenizeRegExpWithOptions = function(){ + var lexer = new nglr.Lexer("/r/g"); + var tokens = lexer.parse(); + var i = 0; + assertEquals(tokens[i].index, 0); + assertEquals(tokens[i].text, 'r'); + assertEquals(tokens[i].flags, 'g'); + assertEquals("rr".match(tokens[i].fn()).length, 2); +}; + +LexerTest.prototype.testTokenizeRegExpWithEscape = function(){ + var lexer = new nglr.Lexer("/\\/\\d/"); + var tokens = lexer.parse(); + var i = 0; + assertEquals(tokens[i].index, 0); + assertEquals(tokens[i].text, '\\/\\d'); + assertEquals("/1".match(tokens[i].fn())[0], '/1'); +}; + +LexerTest.prototype.testIgnoreWhitespace = function(){ + var lexer = new nglr.Lexer("a \t \n \r b"); + var tokens = lexer.parse(); + assertEquals(tokens[0].text, 'a'); + assertEquals(tokens[1].text, 'b'); +}; + +LexerTest.prototype.testRelation = function(){ + var lexer = new nglr.Lexer("! == != < > <= >="); + var tokens = lexer.parse(); + assertEquals(tokens[0].text, '!'); + assertEquals(tokens[1].text, '=='); + assertEquals(tokens[2].text, '!='); + assertEquals(tokens[3].text, '<'); + assertEquals(tokens[4].text, '>'); + assertEquals(tokens[5].text, '<='); + assertEquals(tokens[6].text, '>='); +}; + +LexerTest.prototype.testStatements = function(){ + var lexer = new nglr.Lexer("a;b;"); + var tokens = lexer.parse(); + assertEquals(tokens[0].text, 'a'); + assertEquals(tokens[1].text, ';'); + assertEquals(tokens[2].text, 'b'); + assertEquals(tokens[3].text, ';'); +}; + +ParserTest = TestCase('ParserTest'); + +ParserTest.prototype.testExpressions = function(){ + var scope = new nglr.Scope(); + assertEquals(scope.eval("-1"), -1); + assertEquals(scope.eval("1 + 2.5"), 3.5); + assertEquals(scope.eval("1 + -2.5"), -1.5); + assertEquals(scope.eval("1+2*3/4"), 1+2*3/4); + assertEquals(scope.eval("0--1+1.5"), 0- -1 + 1.5); + assertEquals(scope.eval("-0--1++2*-3/-4"), -0- -1+ +2*-3/-4); + assertEquals(scope.eval("1/2*3"), 1/2*3); +}; + +ParserTest.prototype.testComparison = function(){ + var scope = new nglr.Scope(); + assertEquals(scope.eval("false"), false); + assertEquals(scope.eval("!true"), false); + assertEquals(scope.eval("1==1"), true); + assertEquals(scope.eval("1!=2"), true); + assertEquals(scope.eval("1<2"), true); + assertEquals(scope.eval("1<=1"), true); + assertEquals(scope.eval("1>2"), 1>2); + assertEquals(scope.eval("2>=1"), 2>=1); +}; + +ParserTest.prototype.testLogical = function(){ + var scope = new nglr.Scope(); + assertEquals(scope.eval("0&&2"), 0&&2); + assertEquals(scope.eval("0||2"), 0||2); + assertEquals(scope.eval("0||1&&2"), 0||1&&2); +}; + +ParserTest.prototype.testString = function(){ + var scope = new nglr.Scope(); + assertEquals(scope.eval("'a' + 'b c'"), "ab c"); +}; + +ParserTest.prototype.testFilters = function(){ + angular.filter.substring = function(input, start, end) { + return input.substring(start, end); + }; + + angular.filter.upper = {_case:function(input) { + return input.toUpperCase(); + }}; + var scope = new nglr.Scope(); + try { + scope.eval("1|nonExistant"); + fail(); + } catch (e) { + assertEquals(e, "Function 'nonExistant' at column '3' in '1|nonExistant' is not defined."); + } + scope.set('offset', 3); + assertEquals(scope.eval("'abcd'|upper._case"), "ABCD"); + assertEquals(scope.eval("'abcd'|substring:1:offset"), "bc"); + assertEquals(scope.eval("'abcd'|substring:1:3|upper._case"), "BC"); +}; + +ParserTest.prototype.testScopeAccess = function(){ + var scope = new nglr.Scope(); + scope.set('a', 123); + scope.set('b.c', 456); + assertEquals(scope.eval("a", scope), 123); + assertEquals(scope.eval("b.c", scope), 456); + assertEquals(scope.eval("x.y.z", scope), undefined); +}; + +ParserTest.prototype.testGrouping = function(){ + var scope = new nglr.Scope(); + assertEquals(scope.eval("(1+2)*3"), (1+2)*3); +}; + +ParserTest.prototype.testAssignments = function(){ + var scope = new nglr.Scope(); + assertEquals(scope.eval("a=12"), 12); + assertEquals(scope.get("a"), 12); + + scope = new nglr.Scope(); + assertEquals(scope.eval("x.y.z=123;"), 123); + assertEquals(scope.get("x.y.z"), 123); + + assertEquals(234, scope.eval("a=123; b=234")); + assertEquals(123, scope.get("a")); + assertEquals(234, scope.get("b")); +}; + +ParserTest.prototype.testFunctionCallsNoArgs = function(){ + var scope = new nglr.Scope(); + scope.set('const', function(a,b){return 123;}); + assertEquals(scope.eval("const()"), 123); +}; + +ParserTest.prototype.testFunctionCalls = function(){ + var scope = new nglr.Scope(); + scope.set('add', function(a,b){ + return a+b; + }); + assertEquals(3, scope.eval("add(1,2)")); +}; + +ParserTest.prototype.testCalculationBug = function(){ + var scope = new nglr.Scope(); + scope.set('taxRate', 8); + scope.set('subTotal', 100); + assertEquals(scope.eval("taxRate / 100 * subTotal"), 8); + assertEquals(scope.eval("subTotal * taxRate / 100"), 8); +}; + +ParserTest.prototype.testArray = function(){ + var scope = new nglr.Scope(); + assertEquals(scope.eval("[]").length, 0); + assertEquals(scope.eval("[1, 2]").length, 2); + assertEquals(scope.eval("[1, 2]")[0], 1); + assertEquals(scope.eval("[1, 2]")[1], 2); +}; + +ParserTest.prototype.testArrayAccess = function(){ + var scope = new nglr.Scope(); + assertEquals(scope.eval("[1][0]"), 1); + assertEquals(scope.eval("[[1]][0][0]"), 1); + assertEquals(scope.eval("[].length"), 0); + assertEquals(scope.eval("[1, 2].length"), 2); +}; + +ParserTest.prototype.testObject = function(){ + var scope = new nglr.Scope(); + assertEquals(nglr.toJson(scope.eval("{}")), "{}"); + assertEquals(nglr.toJson(scope.eval("{a:'b'}")), '{"a":"b"}'); + assertEquals(nglr.toJson(scope.eval("{'a':'b'}")), '{"a":"b"}'); + assertEquals(nglr.toJson(scope.eval("{\"a\":'b'}")), '{"a":"b"}'); +}; + +ParserTest.prototype.testObjectAccess = function(){ + var scope = new nglr.Scope(); + assertEquals("WC", scope.eval("{false:'WC', true:'CC'}[false]")); +}; + +ParserTest.prototype.testJSON = function(){ + var scope = new nglr.Scope(); + assertEquals(nglr.toJson(scope.eval("[{}]")), "[{}]"); + assertEquals(nglr.toJson(scope.eval("[{a:[]}, {b:1}]")), '[{"a":[]},{"b":1}]'); +}; + +ParserTest.prototype.testMultippleStatements = function(){ + var scope = new nglr.Scope(); + assertEquals(scope.eval("a=1;b=3;a+b"), 4); + assertEquals(scope.eval(";;1;;"), 1); +}; + +ParserTest.prototype.testParseThrow = function(){ + expectAsserts(1); + var scope = new nglr.Scope(); + scope.set('e', 'abc'); + try { + scope.eval("throw e"); + } catch(e) { + assertEquals(e, 'abc'); + } +}; + +ParserTest.prototype.testMethodsGetDispatchedWithCorrectThis = function(){ + var scope = new nglr.Scope(); + var C = function (){ + this.a=123; + }; + C.prototype.getA = function(){ + return this.a; + }; + + scope.set("obj", new C()); + assertEquals(123, scope.eval("obj.getA()")); +}; +ParserTest.prototype.testMethodsArgumentsGetCorrectThis = function(){ + var scope = new nglr.Scope(); + var C = function (){ + this.a=123; + }; + C.prototype.sum = function(value){ + return this.a + value; + }; + C.prototype.getA = function(){ + return this.a; + }; + + scope.set("obj", new C()); + assertEquals(246, scope.eval("obj.sum(obj.getA())")); +}; + +ParserTest.prototype.testObjectPointsToScopeValue = function(){ + var scope = new nglr.Scope(); + scope.set('a', "abc"); + assertEquals("abc", scope.eval("{a:a}").a); +}; + +ParserTest.prototype.testFieldAccess = function(){ + var scope = new nglr.Scope(); + var fn = function(){ + return {name:'misko'}; + }; + scope.set('a', fn); + assertEquals("misko", scope.eval("a().name")); +}; + +ParserTest.prototype.testArrayIndexBug = function () { + var scope = new nglr.Scope(); + scope.set('items', [{}, {name:'misko'}]); + + assertEquals("misko", scope.eval('items[1].name')); +}; + +ParserTest.prototype.testArrayAssignment = function () { + var scope = new nglr.Scope(); + scope.set('items', []); + + assertEquals("abc", scope.eval('items[1] = "abc"')); + assertEquals("abc", scope.eval('items[1]')); +// Dont know how to make this work.... +// assertEquals("moby", scope.eval('books[1] = "moby"')); +// assertEquals("moby", scope.eval('books[1]')); +}; + +ParserTest.prototype.testFiltersCanBeGrouped = function () { + var scope = new nglr.Scope({name:'MISKO'}); + assertEquals('misko', scope.eval('n = (name|lowercase)')); + assertEquals('misko', scope.eval('n')); +}; + +ParserTest.prototype.testFiltersCanBeGrouped = function () { + var scope = new nglr.Scope({name:'MISKO'}); + assertEquals('misko', scope.eval('n = (name|lowercase)')); + assertEquals('misko', scope.eval('n')); +}; + +ParserTest.prototype.testRemainder = function () { + var scope = new nglr.Scope(); + assertEquals(1, scope.eval('1%2')); +}; + +ParserTest.prototype.testSumOfUndefinedIsNotUndefined = function () { + var scope = new nglr.Scope(); + assertEquals(1, scope.eval('1+undefined')); + assertEquals(1, scope.eval('undefined+1')); +}; + +ParserTest.prototype.testMissingThrowsError = function() { + var scope = new nglr.Scope(); + try { + scope.eval('[].count('); + fail(); + } catch (e) { + assertEquals('Unexpected end of expression: [].count(', e); + } +}; + +ParserTest.prototype.testItShouldParseOnChangeIntoHashSet = function () { + var scope = new nglr.Scope({count:0}); + scope.watch("$anchor.a:count=count+1;$anchor.a:count=count+20;b:count=count+300"); + + scope.watchListeners["$anchor.a"].listeners[0](); + assertEquals(1, scope.get("count")); + scope.watchListeners["$anchor.a"].listeners[1](); + assertEquals(21, scope.get("count")); + scope.watchListeners["b"].listeners[0]({scope:scope}); + assertEquals(321, scope.get("count")); +}; +ParserTest.prototype.testItShouldParseOnChangeBlockIntoHashSet = function () { + var scope = new nglr.Scope({count:0}); + var listeners = {a:[], b:[]}; + scope.watch("a:{count=count+1;count=count+20;};b:count=count+300", + function(n, fn){listeners[n].push(fn);}); + + assertEquals(1, scope.watchListeners.a.listeners.length); + assertEquals(1, scope.watchListeners.b.listeners.length); + scope.watchListeners["a"].listeners[0](); + assertEquals(21, scope.get("count")); + scope.watchListeners["b"].listeners[0](); + assertEquals(321, scope.get("count")); +}; + +ParserTest.prototype.testItShouldParseEmptyOnChangeAsNoop = function () { + var scope = new nglr.Scope(); + scope.watch("", function(){fail();}); +}; + +ParserTest.prototype.testItShouldCreateClosureFunctionWithNoArguments = function () { + var scope = new nglr.Scope(); + var fn = scope.eval("{:value}"); + scope.set("value", 1); + assertEquals(1, fn()); + scope.set("value", 2); + assertEquals(2, fn()); + fn = scope.eval("{():value}"); + assertEquals(2, fn()); +}; + +ParserTest.prototype.testItShouldCreateClosureFunctionWithArguments = function () { + var scope = new nglr.Scope(); + var fn = scope.eval("{(a):value+a}"); + scope.set("value", 1); + assertEquals(11, fn(10)); + scope.set("value", 2); + assertEquals(12, fn(10)); + fn = scope.eval("{(a,b):value+a+b}"); + assertEquals(112, fn(10, 100)); +}; + +ParserTest.prototype.testItShouldHaveDefaultArugument = function(){ + var scope = new nglr.Scope(); + var fn = scope.eval("{:$*2}"); + assertEquals(4, fn(2)); +}; + +ParserTest.prototype.testReturnFunctionsAreNotBound = function(){ + var scope = new nglr.Scope(); + scope.set("$datastore", new nglr.DataStore()); + scope.entity("Group"); + var Group = scope.get("Group"); + assertEquals("eval Group", "function", typeof scope.eval("Group")); + assertEquals("direct Group", "function", typeof Group); + assertEquals("eval Group.all", "function", typeof scope.eval("Group.query")); + assertEquals("direct Group.all", "function", typeof Group.query); +}; + diff --git a/test/ScopeTest.js b/test/ScopeTest.js new file mode 100644 index 00000000..c66a2329 --- /dev/null +++ b/test/ScopeTest.js @@ -0,0 +1,144 @@ +ScopeTest = TestCase('ScopeTest'); + +ScopeTest.prototype.testGetScopeRetrieval = function(){ + var scope = {}; + var form = jQuery("<a><b><c></c></b></a>"); + form.data('scope', scope); + var c = form.find('c'); + assertTrue(scope === c.scope()); +}; + +ScopeTest.prototype.testGetScopeRetrievalIntermediateNode = function(){ + var scope = {}; + var form = jQuery("<a><b><c></c></b></a>"); + form.find("b").data('scope', scope); + var b = form.find('b'); + assertTrue(scope === b.scope()); +}; + +ScopeTest.prototype.testNoScopeDoesNotCauseInfiniteRecursion = function(){ + var form = jQuery("<a><b><c></c></b></a>"); + var c = form.find('c'); + assertTrue(!c.scope()); +}; + +ScopeTest.prototype.testScopeEval = function(){ + var scope = new nglr.Scope({b:345}); + assertEquals(scope.eval('b = 123'), 123); + assertEquals(scope.get('b'), 123); +}; + +ScopeTest.prototype.testScopeFromPrototype = function(){ + var scope = new nglr.Scope({b:123}); + scope.eval('a = b'); + scope.eval('b = 456'); + assertEquals(scope.get('a'), 123); + assertEquals(scope.get('b'), 456); +}; + +ScopeTest.prototype.testSetScopeGet = function(){ + var scope = new nglr.Scope(); + scope.set('a', 987); + assertEquals(scope.get('a'), 987); + assertEquals(scope.eval('a'), 987); +}; + +ScopeTest.prototype.testGetChain = function(){ + var scope = new nglr.Scope({a:{b:987}}); + assertEquals(scope.get('a.b'), 987); + assertEquals(scope.eval('a.b'), 987); +}; + +ScopeTest.prototype.testGetUndefinedChain = function(){ + var scope = new nglr.Scope(); + assertEquals(typeof scope.get('a.b'), 'undefined'); +}; + +ScopeTest.prototype.testSetChain = function(){ + var scope = new nglr.Scope({a:{}}); + scope.set('a.b', 987); + assertEquals(scope.get('a.b'), 987); + assertEquals(scope.eval('a.b'), 987); +}; + +ScopeTest.prototype.testSetGetOnChain = function(){ + var scope = new nglr.Scope(); + scope.set('a.b', 987); + assertEquals(scope.get('a.b'), 987); + assertEquals(scope.eval('a.b'), 987); +}; + +ScopeTest.prototype.testGlobalFunctionAccess =function(){ + window['scopeAddTest'] = function (a, b) {return a+b;}; + var scope = new nglr.Scope({window:window}); + assertEquals(scope.eval('window.scopeAddTest(1,2)'), 3); + + scope.set('add', function (a, b) {return a+b;}); + assertEquals(scope.eval('add(1,2)'), 3); + + scope.set('math.add', function (a, b) {return a+b;}); + assertEquals(scope.eval('math.add(1,2)'), 3); +}; + +ScopeTest.prototype.testValidationEval = function(){ + expectAsserts(4); + var scope = new nglr.Scope(); + angular.validator.testValidator = function(value, expect){ + assertEquals(scope, this.scope); + return value == expect ? null : "Error text"; + }; + + assertEquals("Error text", scope.validate("testValidator:'abc'", 'x')); + assertEquals(null, scope.validate("testValidator:'abc'", 'abc')); + + delete angular.validator['testValidator']; +}; + +ScopeTest.prototype.testCallingNonExistantMethodShouldProduceFriendlyException = function() { + expectAsserts(1); + var scope = new nglr.Scope({obj:{}}); + try { + scope.eval("obj.iDontExist()"); + fail(); + } catch (e) { + assertEquals("Expression 'obj.iDontExist' is not a function.", e); + } +}; + +ScopeTest.prototype.testAccessingWithInvalidPathShouldThrowError = function() { + var scope = new nglr.Scope(); + try { + scope.get('a.{{b}}'); + fail(); + } catch (e) { + assertEquals("Expression 'a.{{b}}' is not a valid expression for accesing variables.", e); + } +}; + +ScopeTest.prototype.testItShouldHave$parent = function() { + var parent = new nglr.Scope({}, "ROOT"); + var child = new nglr.Scope(parent.state); + assertSame("parent", child.state.$parent, parent.state); + assertSame("root", child.state.$root, parent.state); +}; + +ScopeTest.prototype.testItShouldHave$root = function() { + var scope = new nglr.Scope({}, "ROOT"); + assertSame(scope.state.$root, scope.state); +}; + +ScopeTest.prototype.testItShouldBuildPathOnUndefined = function(){ + var scope = new nglr.Scope({}, "ROOT"); + scope.setEval("a.$b.c", 1); + assertJsonEquals({$b:{c:1}}, scope.get("a")); +}; + +ScopeTest.prototype.testItShouldMapUnderscoreFunctions = function(){ + var scope = new nglr.Scope({}, "ROOT"); + scope.set("a", [1,2,3]); + assertEquals('function', typeof scope.get("a.$size")); + scope.eval("a.$includeIf(4,true)"); + assertEquals(4, scope.get("a.$size")()); + assertEquals(4, scope.eval("a.$size()")); + assertEquals('undefined', typeof scope.get("a.dontExist")); +}; diff --git a/test/ServerTest.js b/test/ServerTest.js new file mode 100644 index 00000000..d1f662f9 --- /dev/null +++ b/test/ServerTest.js @@ -0,0 +1,42 @@ +ServerTest = TestCase("ServerTest"); +ServerTest.prototype.testBreakLargeRequestIntoPackets = function() { + var log = ""; + var server = new nglr.Server("http://server", function(url){ + log += "|" + url; + }); + server.maxSize = 30; + server.uuid = "uuid"; + server.request("POST", "/data/database", {}, function(code, r){ + assertEquals(200, code); + assertEquals("response", r); + }); + nglr.uuid0("response"); + assertEquals( + "|http://server/$/uuid0/2/1?h=eyJtIjoiUE9TVCIsInAiOnt9LCJ1Ij" + + "|http://server/$/uuid0/2/2?h=oiL2RhdGEvZGF0YWJhc2UifQ==", + log); +}; + +ServerTest.prototype.testItShouldEncodeUsingUrlRules = function() { + var server = new nglr.Server("http://server"); + assertEquals("fn5-fn5-", server.base64url("~~~~~~")); + assertEquals("fn5_fn5_", server.base64url("~~\u007f~~\u007f")); +}; + +FrameServerTest = TestCase("FrameServerTest"); + +FrameServerTest.prototype = { + testRead:function(){ + var window = {name:'$DATASET:"MyData"'}; + var server = new nglr.FrameServer(window); + server.read(); + assertEquals("MyData", server.data); + }, + testWrite:function(){ + var window = {}; + var server = new nglr.FrameServer(window); + server.data = "TestData" + server.write(); + assertEquals('$DATASET:"TestData"', window.name); + } +}; diff --git a/test/UsersTest.js b/test/UsersTest.js new file mode 100644 index 00000000..c808885c --- /dev/null +++ b/test/UsersTest.js @@ -0,0 +1,26 @@ +// Copyright (C) 2008,2009 BRAT Tech LLC + +UsersTest = TestCase("UsersTest"); + +UsersTest.prototype = { + setUp:function(){}, + + tearDown:function(){}, + + testItShouldFetchCurrentUser:function(){ + expectAsserts(5); + var user; + var users = new nglr.Users({request:function(method, url, request, callback){ + assertEquals("GET", method); + assertEquals("/account.json", url); + assertEquals("{}", nglr.toJson(request)); + callback(200, {$status_code:200, user:{name:'misko'}}); + }}); + users.fetchCurrentUser(function(u){ + user = u; + assertEquals("misko", u.name); + assertEquals("misko", users.current.name); + }); + } + +}; diff --git a/test/ValidatorsTest.js b/test/ValidatorsTest.js new file mode 100644 index 00000000..22c7f390 --- /dev/null +++ b/test/ValidatorsTest.js @@ -0,0 +1,65 @@ +ValidatorTest = TestCase('ValidatorTest'); + +ValidatorTest.prototype.testRegexp = function() { + assertEquals(angular.validator.regexp("abc", /x/, "E1"), "E1"); + assertEquals(angular.validator.regexp("abc", '/x/'), + "Value does not match expected format /x/."); + assertEquals(angular.validator.regexp("ab", '^ab$'), null); + assertEquals(angular.validator.regexp("ab", '^axb$', "E3"), "E3"); +}; + +ValidatorTest.prototype.testNumber = function() { + assertEquals(angular.validator.number("ab"), "Value is not a number."); + assertEquals(angular.validator.number("-0.1",0), "Value can not be less than 0."); + assertEquals(angular.validator.number("10.1",0,10), "Value can not be greater than 10."); + assertEquals(angular.validator.number("1.2"), null); + assertEquals(angular.validator.number(" 1 ", 1, 1), null); +}; + +ValidatorTest.prototype.testInteger = function() { + assertEquals(angular.validator.integer("ab"), "Value is not a number."); + assertEquals(angular.validator.integer("1.1"), "Value is not a whole number."); + assertEquals(angular.validator.integer("-1",0), "Value can not be less than 0."); + assertEquals(angular.validator.integer("11",0,10), "Value can not be greater than 10."); + assertEquals(angular.validator.integer("1"), null); + assertEquals(angular.validator.integer(" 1 ", 1, 1), null); +}; + +ValidatorTest.prototype.testDate = function() { + var error = "Value is not a date. (Expecting format: 12/31/2009)."; + assertEquals(angular.validator.date("ab"), error); + assertEquals(angular.validator.date("12/31/2009"), null); +}; + +ValidatorTest.prototype.testPhone = function() { + var error = "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly."; + assertEquals(angular.validator.phone("ab"), error); + assertEquals(null, angular.validator.phone("1(408)757-3023")); + assertEquals(null, angular.validator.phone("+421 (0905) 933 297")); + assertEquals(null, angular.validator.phone("+421 0905 933 297")); +}; + +ValidatorTest.prototype.testSSN = function() { + var error = "SSN needs to be in 999-99-9999 format."; + assertEquals(angular.validator.ssn("ab"), error); + assertEquals(angular.validator.ssn("123-45-6789"), null); +}; + +ValidatorTest.prototype.testURL = function() { + var error = "URL needs to be in http://server[:port]/path format."; + assertEquals(angular.validator.url("ab"), error); + assertEquals(angular.validator.url("http://server:123/path"), null); +}; + +ValidatorTest.prototype.testEmail = function() { + var error = "Email needs to be in username@host.com format."; + assertEquals(error, angular.validator.email("ab")); + assertEquals(null, angular.validator.email("misko@hevery.com")); +}; + +ValidatorTest.prototype.testJson = function() { + assertNotNull(angular.validator.json("'")); + assertNotNull(angular.validator.json("''X")); + assertNull(angular.validator.json("{}")); +}; + diff --git a/test/WidgetsTest.js b/test/WidgetsTest.js new file mode 100644 index 00000000..a245abda --- /dev/null +++ b/test/WidgetsTest.js @@ -0,0 +1,269 @@ +WidgetTest = TestCase('WidgetTest'); + +WidgetTest.prototype.testRequired = function () { + var view = $('<input name="a" ng-required>'); + var scope = new nglr.Scope({$invalidWidgets:[]}); + var cntl = new nglr.TextController(view[0], 'a'); + cntl.updateView(scope); + assertTrue(view.hasClass('ng-validation-error')); + assertEquals("Required Value", view.attr('ng-error')); + scope.set('a', 'A'); + cntl.updateView(scope); + assertFalse(view.hasClass('ng-validation-error')); + assertEquals("undefined", typeof view.attr('ng-error')); +}; + +WidgetTest.prototype.testValidator = function () { + var view = $('<input name="a" ng-validate="testValidator:\'ABC\'">'); + var scope = new nglr.Scope({$invalidWidgets:[]}); + var cntl = new nglr.TextController(view[0], 'a'); + angular.validator.testValidator = function(value, expect){ + return value == expect ? null : "Error text"; + }; + + scope.set('a', ''); + cntl.updateView(scope); + assertEquals(view.hasClass('ng-validation-error'), false); + assertEquals(null, view.attr('ng-error')); + + scope.set('a', 'X'); + cntl.updateView(scope); + assertEquals(view.hasClass('ng-validation-error'), true); + assertEquals(view.attr('ng-error'), "Error text"); + assertEquals("Error text", view.attr('ng-error')); + + scope.set('a', 'ABC'); + cntl.updateView(scope); + assertEquals(view.hasClass('ng-validation-error'), false); + assertEquals(view.attr('ng-error'), null); + assertEquals(null, view.attr('ng-error')); + + delete angular.validator['testValidator']; +}; + +WidgetTest.prototype.testRequiredValidator = function () { + var view = $('<input name="a" ng-required ng-validate="testValidator:\'ABC\'">'); + var scope = new nglr.Scope({$invalidWidgets:[]}); + var cntl = new nglr.TextController(view[0], 'a'); + angular.validator.testValidator = function(value, expect){ + return value == expect ? null : "Error text"; + }; + + scope.set('a', ''); + cntl.updateView(scope); + assertEquals(view.hasClass('ng-validation-error'), true); + assertEquals("Required Value", view.attr('ng-error')); + + scope.set('a', 'X'); + cntl.updateView(scope); + assertEquals(view.hasClass('ng-validation-error'), true); + assertEquals("Error text", view.attr('ng-error')); + + scope.set('a', 'ABC'); + cntl.updateView(scope); + assertEquals(view.hasClass('ng-validation-error'), false); + assertEquals(null, view.attr('ng-error')); + + delete angular.validator['testValidator']; +}; + +TextController = TestCase("TextController"); + +TextController.prototype.testDatePicker = function() { + var input = $('<input type="text" ng-widget="datepicker">'); + input.data('scope', new nglr.Scope()); + var body = $(document.body); + body.append(input); + var binder = new nglr.Binder(input[0], new nglr.WidgetFactory()); + assertTrue('before', input.data('datepicker') === undefined); + binder.compile(); + assertTrue('after', input.data('datepicker') !== null); + assertTrue(body.html(), input.hasClass('hasDatepicker')); +}; + +RepeaterUpdater = TestCase("RepeaterUpdater"); + +RepeaterUpdater.prototype.testRemoveThenAdd = function() { + var view = $("<div><span/></div>"); + var template = function () { + return $("<li/>"); + }; + var repeater = new nglr.RepeaterUpdater(view.find("span"), "a in b", template, ""); + var scope = new nglr.Scope(); + scope.set('b', [1,2]); + + repeater.updateView(scope); + + scope.set('b', []); + repeater.updateView(scope); + + scope.set('b', [1]); + repeater.updateView(scope); + assertEquals(1, view.find("li").size()); +}; + +RepeaterUpdater.prototype.testShouldBindWidgetOnRepeaterClone = function(){ + //fail(); +}; + +RepeaterUpdater.prototype.testShouldThrowInformativeSyntaxError= function(){ + expectAsserts(1); + try { + var repeater = new nglr.RepeaterUpdater(null, "a=b"); + } catch (e) { + assertEquals("Expected ng-repeat in form of 'item in collection' but got 'a=b'.", e); + } +}; + +SelectControllerTest = TestCase("SelectControllerTest"); + +SelectControllerTest.prototype.testShouldUpdateModelNullOnNothingSelected = function(){ + var scope = new nglr.Scope(); + var view = {selectedIndex:-1, options:[]}; + var cntl = new nglr.SelectController(view, 'abc'); + cntl.updateModel(scope); + assertNull(scope.get('abc')); +}; + +SelectControllerTest.prototype.testShouldUpdateModelWhenNothingSelected = function(){ + var scope = new nglr.Scope(); + var view = {value:'123'}; + var cntl = new nglr.SelectController(view, 'abc'); + cntl.updateView(scope); + assertEquals("123", scope.get('abc')); +}; + +BindUpdaterTest = TestCase("BindUpdaterTest"); + +BindUpdaterTest.prototype.testShouldDisplayNothingForUndefined = function () { + var view = $('<span />'); + var controller = new nglr.BindUpdater(view[0], "{{a}}"); + var scope = new nglr.Scope(); + + scope.set('a', undefined); + controller.updateView(scope); + assertEquals("", view.text()); + + scope.set('a', null); + controller.updateView(scope); + assertEquals("", view.text()); +}; + +BindUpdaterTest.prototype.testShouldDisplayJsonForNonStrings = function () { + var view = $('<span />'); + var controller = new nglr.BindUpdater(view[0], "{{obj}}"); + + controller.updateView(new nglr.Scope({obj:[]})); + assertEquals("[]", view.text()); + + controller.updateView(new nglr.Scope({obj:{text:'abc'}})); + assertEquals('abc', nglr.fromJson(view.text()).text); +}; + + +BindUpdaterTest.prototype.testShouldInsertHtmlNode = function () { + var view = $('<span />'); + var controller = new nglr.BindUpdater(view[0], "<fake>&{{obj}}</fake>"); + var scope = new nglr.Scope(); + + scope.set("obj", $('<div>myDiv</div>')[0]); + controller.updateView(scope); + assertEquals("<fake>&myDiv</fake>", view.text()); +}; + + +BindUpdaterTest.prototype.testShouldDisplayTextMethod = function () { + var view = $('<div />'); + var controller = new nglr.BindUpdater(view[0], "{{obj}}"); + var scope = new nglr.Scope(); + + scope.set("obj", new angular.filter.Meta({text:function(){return "abc";}})); + controller.updateView(scope); + assertEquals("abc", view.text()); + + scope.set("obj", new angular.filter.Meta({text:"123"})); + controller.updateView(scope); + assertEquals("123", view.text()); + + scope.set("obj", {text:"123"}); + controller.updateView(scope); + assertEquals("123", nglr.fromJson(view.text()).text); +}; + +BindUpdaterTest.prototype.testShouldDisplayHtmlMethod = function () { + var view = $('<div />'); + var controller = new nglr.BindUpdater(view[0], "{{obj}}"); + var scope = new nglr.Scope(); + + scope.set("obj", new angular.filter.Meta({html:function(){return "a<div>b</div>c";}})); + controller.updateView(scope); + assertEquals("abc", view.text()); + + scope.set("obj", new angular.filter.Meta({html:"1<div>2</div>3"})); + controller.updateView(scope); + assertEquals("123", view.text()); + + scope.set("obj", {html:"123"}); + controller.updateView(scope); + assertEquals("123", nglr.fromJson(view.text()).html); +}; + +BindUpdaterTest.prototype.testUdateBoolean = function() { + var view = $('<div />'); + var controller = new nglr.BindUpdater(view[0], "{{true}}, {{false}}"); + controller.updateView(new nglr.Scope()); + assertEquals('true, false', view.text()); +}; + +BindAttrUpdaterTest = TestCase("BindAttrUpdaterTest"); + +BindAttrUpdaterTest.prototype.testShouldLoadBlankImageWhenBindingIsUndefined = function () { + var view = $('<img />'); + var controller = new nglr.BindAttrUpdater(view[0], {src: '{{imageUrl}}'}); + + var scope = new nglr.Scope(); + scope.set('imageUrl', undefined); + scope.set('config.server', 'http://server'); + + controller.updateView(scope); + assertEquals("http://server/images/blank.gif", view.attr('src')); +}; + +RepeaterUpdaterTest = TestCase("RepeaterUpdaterTest"); +RepeaterUpdaterTest.prototype.testShouldNotDieWhenRepeatExpressionIsNull = function() { + var rep = new nglr.RepeaterUpdater(null, "$item in items", null, null); + var scope = new nglr.Scope(); + scope.set('items', undefined); + rep.updateView(scope); +}; + +RepeaterUpdaterTest.prototype.testShouldIterateOverKeys = function() { + var rep = new nglr.RepeaterUpdater(null, "($k,_v) in items", null, null); + assertEquals("items", rep.iteratorExp); + assertEquals("_v", rep.valueExp); + assertEquals("$k", rep.keyExp); +}; + +EvalUpdaterTest = TestCase("EvalUpdaterTest"); +EvalUpdaterTest.prototype.testEvalThrowsException = function(){ + var view = $('<div/>'); + var eval = new nglr.EvalUpdater(view[0], 'undefined()'); + + eval.updateView(new nglr.Scope()); + assertTrue(!!view.attr('ng-error')); + assertTrue(view.hasClass('ng-exception')); + + eval.exp = "1"; + eval.updateView(new nglr.Scope()); + assertFalse(!!view.attr('ng-error')); + assertFalse(view.hasClass('ng-exception')); +}; + +RadioControllerTest = TestCase("RadioController"); +RadioControllerTest.prototype.testItShouldTreatTrueStringAsBoolean = function () { + var view = $('<input type="radio" name="select" value="true"/>'); + var radio = new nglr.RadioController(view[0], 'select'); + var scope = new nglr.Scope({select:true}); + radio.updateView(scope); + assertTrue(view[0].checked); +}; diff --git a/test/XSitePostTest.js b/test/XSitePostTest.js new file mode 100644 index 00000000..8a3e4d6f --- /dev/null +++ b/test/XSitePostTest.js @@ -0,0 +1,47 @@ +XSitePost = TestCase("XSitePost"); + +var e = function(text){ return Base64.encode(text); }; + +XSitePost.prototype.testMessageReceived = function () { + expectAsserts(4); + var xPost = new nglr.XSitePost(); + xPost.baseUrl = "http://getangular.test"; + xPost.post = function(url, request, callback){ + assertEquals('http://getangular.test/url', url); + assertEquals('abc', request.a); + assertEquals('xyz', request.x); + }; + xPost.incomingFragment('#id;0;1;'+e('/url')+':a:'+e('abc')+':x:'+e('xyz')); + assertEquals('{}', nglr.toJson(xPost.inQueue)); +}; + +XSitePost.prototype.testMessageReceivedInParts = function () { + expectAsserts(5); + var xPost = new nglr.XSitePost(); + xPost.baseUrl = "http://getangular.test"; + xPost.post = function(url, request, callback){ + assertEquals('http://getangular.test/url', url); + assertEquals('abc', request.a); + assertEquals('xyz', request.x); + }; + xPost.incomingFragment('#id;1;2;:x:'+e('xyz')); + assertNotSame('{}', nglr.toJson(xPost.inQueue)); + xPost.incomingFragment('#id;0;2;'+e('/url')+':a:'+e('abc')); + assertEquals('{}', nglr.toJson(xPost.inQueue)); +}; + +XSitePost.prototype.testPostResponsIsEnqueued = function () { + var xPost = new nglr.XSitePost(); + xPost.maxMsgSize = 11; + xPost.response("id", "response", "status"); + + assertEquals('["id:0:2:cmVzcG9uc2U","id:1:2:="]', + nglr.toJson(xPost.outQueue)); +}; + +XSitePost.prototype.testPush = function () { + var window = {}; + var xPost = new nglr.XSitePost(window); + xPost.response("id", "response", "status"); + assertEquals('id:0:1:cmVzcG9uc2U=', xPost.outQueue[0]); +}; diff --git a/test/formsTest.js b/test/formsTest.js new file mode 100644 index 00000000..e834e938 --- /dev/null +++ b/test/formsTest.js @@ -0,0 +1,22 @@ +nglrTest = TestCase('nglrTest'); + +nglrTest.prototype.testShiftBind = function(){ + expectAsserts(3); + nglr.shiftBind('this', function(target, arg) { + assertEquals(this, 'this'); + assertEquals(target, 'target'); + assertEquals(arg, 'arg'); + }).apply('target', ['arg']); +}; + +nglrTest.prototype.testBind = function(){ + expectAsserts(2); + nglr.bind('this', function(arg) { + assertEquals(this, 'this'); + assertEquals(arg, 'arg'); + }).apply('XXX', ['arg']); +}; + + + + diff --git a/test/test/StepsTest.js b/test/test/StepsTest.js new file mode 100644 index 00000000..9d64d0a9 --- /dev/null +++ b/test/test/StepsTest.js @@ -0,0 +1,7 @@ +StepsTest = TestCase("StepsTest"); + +StepsTest.prototype.testGivenDataset=function(){ + var self = {frame:{}, dataset:[]}; + angular.test.GIVEN.dataset.call(self); + assertEquals('$DATASET:{"dataset":[]}', self.frame.name); +}; diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js new file mode 100644 index 00000000..5fca3524 --- /dev/null +++ b/test/testabilityPatch.js @@ -0,0 +1,129 @@ +TestCase = function(name) { return jstestdriver.testCaseManager.TestCase(name); }; + +HIDDEN = jQuery.browser.msie ? + '' : + jQuery.browser.safari ? + ' style="display: none; "' : + ' style="display: none;"'; + +nglr.msie = jQuery.browser.msie; +nglr.alert = function(msg) {jstestdriver.console.log("ALERT: " + msg);}; + +function noop(){} + +jstd = jstestdriver; + +function html(content) { + return jQuery("<div></div>").html(content); +} + +function report(reportTest){ + $("#tests").children().each(function(i){ + var success = this.className == "pass"; + var strong = this.firstChild; + var msg = strong.firstChild.nodeValue; + var parts = msg.split(" module: "); + var module = parts[0]; + var name = parts[1].replace(/ *$/, ""); + reportTest(success, module, name, this.nodeValue); + }); +} + +MockUrlWatcher = function() { + this.url = "http://server"; +}; +MockUrlWatcher.prototype.getUrl = function(){ + return this.url; +}; +MockUrlWatcher.prototype.setUrl = function(url){ + this.url = url; +}; + +jQuery.fn.sortedHtml = function() { + var html = ""; + var toString = function(index, node) { + node = node || this; + if (node.nodeName == "#text") { + html += nglr.escapeHtml(node.nodeValue); + } else { + html += '<' + node.nodeName.toLowerCase(); + var attributes = node.attributes || []; + var attrs = []; + for(var i=0; i<attributes.length; i++) { + var attr = attributes[i]; + if(attr.name.match(/^ng-/) || + attr.value && + attr.value !='null' && + attr.value !='auto' && + attr.value !='false' && + attr.value !='inherit' && + attr.value !='0' && + attr.name !='loop' && + attr.name !='maxLength' && + attr.name !='size' && + attr.name !='start' && + attr.name !='tabIndex' && + attr.name.substr(0, 6) != 'jQuery') { + // in IE we need to check for all of these. + attrs.push(' ' + attr.name + '="' + attr.value + '"'); + } + } + attrs.sort(); + html += attrs.join(''); + html += '>'; + var children = node.childNodes; + for(var j=0; j<children.length; j++) { + toString(j, children[j]); + } + html += '</' + node.nodeName.toLowerCase() + '>'; + } + }; + this.children().each(toString); + return html; +}; + +function encode64(obj){ + return Base64.encode(nglr.toJson(obj)); +} + +function decode64(base64){ + return nglr.fromJson(Base64.decode(base64)); +} + +nglr.Loader.prototype.configureJQueryPlugins(); + +function assertHidden(node) { + var display = node.css('display'); + assertEquals("Node should be hidden but vas visible: " + node.sortedHtml(), 'none', display); +} + +function assertVisible(node) { + var display = node.css('display'); + if (display == 'block') display = ""; + assertEquals("Node should be visible but vas hidden: " + node.sortedHtml(), '', display); +} + +function assertJsonEquals(expected, actual) { + assertEquals(nglr.toJson(expected), nglr.toJson(actual)); +} + +function assertUndefined(value) { + assertEquals('undefined', typeof value); +} + +function assertDefined(value) { + assertTrue(nglr.toJson(value), !!value); +} + +function assertThrows(error, fn){ + var exception = null; + try { + fn(); + } catch(e) { + exception = e; + } + if (!exception) { + fail("Expecting exception, none thrown"); + } + assertEquals(error, exception); +}
\ No newline at end of file |
