From 4f78fd692c0ec51241476e6be9a4df06cd62fdd6 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Thu, 8 Sep 2011 13:56:29 -0700 Subject: feat(forms): new and improved forms --- test/service/formFactorySpec.js | 218 +++++++++++++++++++++++++++++++++++++ test/service/invalidWidgetsSpec.js | 41 ------- test/service/routeSpec.js | 20 +++- 3 files changed, 232 insertions(+), 47 deletions(-) create mode 100644 test/service/formFactorySpec.js delete mode 100644 test/service/invalidWidgetsSpec.js (limited to 'test/service') diff --git a/test/service/formFactorySpec.js b/test/service/formFactorySpec.js new file mode 100644 index 00000000..5223cede --- /dev/null +++ b/test/service/formFactorySpec.js @@ -0,0 +1,218 @@ +'use strict'; + +describe('$formFactory', function(){ + + var rootScope; + var formFactory; + + beforeEach(function(){ + rootScope = angular.scope(); + formFactory = rootScope.$service('$formFactory'); + }); + + + it('should have global form', function(){ + expect(formFactory.rootForm).toBeTruthy(); + expect(formFactory.rootForm.$createWidget).toBeTruthy(); + }); + + + describe('new form', function(){ + var form; + var scope; + var log; + + function WidgetCtrl($formFactory){ + this.$formFactory = $formFactory; + log += ''; + this.$render = function(){ + log += '$render();'; + }; + this.$on('$validate', function(e){ + log += '$validate();'; + }); + } + + WidgetCtrl.$inject = ['$formFactory']; + + WidgetCtrl.prototype = { + getFormFactory: function() { + return this.$formFactory; + } + }; + + beforeEach(function(){ + log = ''; + scope = rootScope.$new(); + form = formFactory(scope); + }); + + describe('$createWidget', function(){ + var widget; + + beforeEach(function() { + widget = form.$createWidget({ + scope:scope, + model:'text', + alias:'text', + controller:WidgetCtrl}); + }); + + + describe('data flow', function(){ + it('should have status properties', function(){ + expect(widget.$error).toEqual({}); + expect(widget.$valid).toBe(true); + expect(widget.$invalid).toBe(false); + }); + + + it('should update view when model changes', function(){ + scope.text = 'abc'; + scope.$digest(); + expect(log).toEqual('$validate();$render();'); + expect(widget.$modelValue).toEqual('abc'); + + scope.text = 'xyz'; + scope.$digest(); + expect(widget.$modelValue).toEqual('xyz'); + + }); + + + it('should have controller prototype methods', function(){ + expect(widget.getFormFactory()).toEqual(formFactory); + }); + }); + + + describe('validation', function(){ + it('should update state on error', function(){ + widget.$emit('$invalid', 'E'); + expect(widget.$valid).toEqual(false); + expect(widget.$invalid).toEqual(true); + + widget.$emit('$valid', 'E'); + expect(widget.$valid).toEqual(true); + expect(widget.$invalid).toEqual(false); + }); + + + it('should have called the model setter before the validation', function(){ + var modelValue; + widget.$on('$validate', function(){ + modelValue = scope.text; + }); + widget.$emit('$viewChange', 'abc'); + expect(modelValue).toEqual('abc'); + }); + + + describe('form', function(){ + it('should invalidate form when widget is invalid', function(){ + expect(form.$error).toEqual({}); + expect(form.$valid).toEqual(true); + expect(form.$invalid).toEqual(false); + + widget.$emit('$invalid', 'REASON'); + + expect(form.$error.REASON).toEqual([widget]); + expect(form.$valid).toEqual(false); + expect(form.$invalid).toEqual(true); + + var widget2 = form.$createWidget({ + scope:scope, model:'text', + alias:'text', + controller:WidgetCtrl + }); + widget2.$emit('$invalid', 'REASON'); + + expect(form.$error.REASON).toEqual([widget, widget2]); + expect(form.$valid).toEqual(false); + expect(form.$invalid).toEqual(true); + + widget.$emit('$valid', 'REASON'); + + expect(form.$error.REASON).toEqual([widget2]); + expect(form.$valid).toEqual(false); + expect(form.$invalid).toEqual(true); + + widget2.$emit('$valid', 'REASON'); + + expect(form.$error).toEqual({}); + expect(form.$valid).toEqual(true); + expect(form.$invalid).toEqual(false); + }); + }); + + }); + + describe('id assignment', function(){ + it('should default to name expression', function(){ + expect(form.text).toEqual(widget); + }); + + + it('should use ng:id', function() { + widget = form.$createWidget({ + scope:scope, + model:'text', + alias:'my.id', + controller:WidgetCtrl + }); + expect(form['my.id']).toEqual(widget); + }); + + + it('should not override existing names', function() { + var widget2 = form.$createWidget({ + scope:scope, + model:'text', + alias:'text', + controller:WidgetCtrl + }); + expect(form.text).toEqual(widget); + expect(widget2).not.toEqual(widget); + }); + }); + + describe('dealocation', function() { + it('should dealocate', function() { + var widget2 = form.$createWidget({ + scope:scope, + model:'text', + alias:'myId', + controller:WidgetCtrl + }); + expect(form.myId).toEqual(widget2); + var widget3 = form.$createWidget({ + scope:scope, + model:'text', + alias:'myId', + controller:WidgetCtrl + }); + expect(form.myId).toEqual(widget2); + + widget3.$destroy(); + expect(form.myId).toEqual(widget2); + + widget2.$destroy(); + expect(form.myId).toBeUndefined(); + }); + + + it('should remove invalid fields from errors, when child widget removed', function(){ + widget.$emit('$invalid', 'MyError'); + + expect(form.$error.MyError).toEqual([widget]); + expect(form.$invalid).toEqual(true); + + widget.$destroy(); + + expect(form.$error.MyError).toBeUndefined(); + expect(form.$invalid).toEqual(false); + }); + }); + }); + }); +}); diff --git a/test/service/invalidWidgetsSpec.js b/test/service/invalidWidgetsSpec.js deleted file mode 100644 index fe7efe38..00000000 --- a/test/service/invalidWidgetsSpec.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -describe('$invalidWidgets', function() { - var scope; - - beforeEach(function(){ - scope = angular.scope(); - }); - - - afterEach(function(){ - dealoc(scope); - }); - - - it("should count number of invalid widgets", function(){ - var element = jqLite(''); - jqLite(document.body).append(element); - scope = compile(element)(); - var $invalidWidgets = scope.$service('$invalidWidgets'); - expect($invalidWidgets.length).toEqual(1); - - scope.price = 123; - scope.$digest(); - expect($invalidWidgets.length).toEqual(0); - - scope.$element.remove(); - scope.price = 'abc'; - scope.$digest(); - expect($invalidWidgets.length).toEqual(0); - - jqLite(document.body).append(scope.$element); - scope.price = 'abcd'; //force revalidation, maybe this should be done automatically? - scope.$digest(); - expect($invalidWidgets.length).toEqual(1); - - jqLite(document.body).html(''); - scope.$digest(); - expect($invalidWidgets.length).toEqual(0); - }); -}); diff --git a/test/service/routeSpec.js b/test/service/routeSpec.js index c8c8cbeb..5aba2a1f 100644 --- a/test/service/routeSpec.js +++ b/test/service/routeSpec.js @@ -152,18 +152,18 @@ describe('$route', function() { $location.path('/foo'); scope.$digest(); - expect(scope.$$childHead).toBeTruthy(); - expect(scope.$$childHead).toEqual(scope.$$childTail); + expect(scope.$$childHead.$id).toBeTruthy(); + expect(scope.$$childHead.$id).toEqual(scope.$$childTail.$id); $location.path('/bar'); scope.$digest(); - expect(scope.$$childHead).toBeTruthy(); - expect(scope.$$childHead).toEqual(scope.$$childTail); + expect(scope.$$childHead.$id).toBeTruthy(); + expect(scope.$$childHead.$id).toEqual(scope.$$childTail.$id); $location.path('/baz'); scope.$digest(); - expect(scope.$$childHead).toBeTruthy(); - expect(scope.$$childHead).toEqual(scope.$$childTail); + expect(scope.$$childHead.$id).toBeTruthy(); + expect(scope.$$childHead.$id).toEqual(scope.$$childTail.$id); $location.path('/'); scope.$digest(); @@ -172,6 +172,14 @@ describe('$route', function() { }); + it('should infer arguments in injection', function() { + $route.when('/test', {controller: function($route){ this.$route = $route; }}); + $location.path('/test'); + scope.$digest(); + expect($route.current.scope.$route).toBe($route); + }); + + describe('redirection', function() { it('should support redirection via redirectTo property by updating $location', function() { var onChangeSpy = jasmine.createSpy('onChange'); -- cgit v1.2.3