指令范围内的表单没有命名属性

时间:2016-05-10 19:52:25

标签: angularjs unit-testing angularjs-directive jasmine karma-jasmine

我使用单元测试来测试我在AngularJS中编写的代码。为此,我使用业力,而karma.conf正在做它所拥有的。

在测试包含表单的指令(使用它自己的控制器)时,我无法访问任何表单的命名输入。这意味着我无法模拟正在填写的表单(使用form.namedproperty。$ setViewValue(val)语法)

以下是我目前使用的代码: directive.html:

<form ng-submit="vm.login(loginForm, vm.user)" name="loginForm" novalidate class="css-form" >
<div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
    <h4 class="modal-title" id="myModalLabel">Aanmelden op HR-App</h4>
</div>
<div class="modal-body">
    <pre>{{vm.user | json}}</pre>
    <div class="form-group"  ng-class="{ 'has-error': loginForm.username.$invalid && (loginForm.username.$dirty || vm.submitted), 'has-success': loginForm.username.$valid}">
        <label class="control-label">Windows Login Name</label>
        <div class="input-group">
            <span class="input-group-addon" id="basic-addon1">@</span>
            <input required ng-model="vm.user.username" type="text" name="username" class="form-control" placeholder="e.g. jMetdepet" aria-describedby="basic-addon1">
        </div>
        <div ng-show="loginForm.username.$invalid && vm.submitted && !vm.error">
            <span class="help-block">Vul een geldige username in</span>
        </div>
    </div>
    <div class="form-group" ng-class="{'has-error': loginForm.pass.$invalid && (loginForm.pass.$dirty || vm.submitted), 'has-success' :  loginForm.pass.$valid}">
        <label class="control-label">Wachtwoord</label>
        <input required ng-model="vm.user.pass" type="password" name="pass" class="form-control" placeholder="Wachtwoord">
        <div ng-show="loginForm.pass.$invalid && vm.submitted && !vm.error">
            <span class="help-block">Vul een geldig wachtwoord in</span>
        </div>
    </div>
    <div ng-show="vm.error">Uw gebruikersnaam of wachtwoord is verkeerd.</div>
    <div class="modal-footer">
    <button type="submit" class="btn btn-default"><span ng-show="vm.busy" class="glyphicon spinner-icon red"></span> Aanmelden</button>
</div>
</div>

这是我的html为我的指令,我使用templateUrl调用。我使用的表单是loginForm,它有两个命名输入:username&amp;通过。这些是我无法在任何地方找到的输入......

directive.js:

(function () {
    "use strict";
    angular
            .module("app.dashboard")
            .directive('myLdapLogin', loginDirective)
            .controller('my-ldapLoginController', ctrl);
    /*
     * This a directive to be used as an element. The directive name is myLdapLogin which compiles into
     * my-ldap-login as element.
     * This directive is a login form that handles the login independantly from the host(the controller that uses the directive)
     * This method allows us to seperate logic into smaller, scalable controllers which are easier to maintain
     */
    function loginDirective() {
        return {
            restrict: 'E',
            templateUrl: 'app/components/login/ldaplogin.directive.html',
            replace: true, // Replace with the template below
            scope: true,
            bindToController: {
//                selected: '&', //functions
//                images: '=', //2 waybinded vars
//                id: '@' //one way binded var
            },
            controller: "my-ldapLoginController",
            controllerAs: 'vm',
        };
    }
    ;
    /*
     * This directive has its own controller
     * The controller is dependant on the ErrorService en AuthenticationService. Therefore we use dependancy injection
     * to call these services (angular factories)
     */
    ctrl.$inject = ["AuthenticationService", "ErrorService"];
    /*
     * @param {Object} AuthenticationService: A service that provides authentication methods
     * @param {Object} ErrorService: A service that provides showing error methods
     */
    function ctrl(AuthenticationService, ErrorService) {
        var vm = this;
        vm.submitted = false;
        vm.error = false;
        vm.user = {};
        vm.login = login;

        /*
         * The invoked method from our form.
         * Will call the Authentication service to login and provide feedback to te user.
         * The API will return a res in the form off:
         * {error:true, message:... code:...} in case it has failed or
         * {error:false,data:...} if succesfull
         */
        function login(form, model) {
            vm.error = false;
            vm.submitted = true;
            if (form.$valid) {
                vm.busy = true;
                AuthenticationService.login(model.username, model.pass).then(function (res) {
                    vm.user = res;
                    console.log(res);
                    vm.submitted = false;
                    vm.busy = false;
                    form.username.$dirty = false;
                    form.pass.$dirty = false;
                }, function (err) {
                    vm.busy = false;
                    //fout 101 = invalid login parameters
                    if (err.code === 101)
                        vm.error = true;
                    else
                        ErrorService.createError(err.message);
                }
                );
            }
        }
    }

})();

在我的HTML中,我定义了submit以包含loginForm,以检查它是否在我的指令控制器中有效。我正在使用可以从模块app.dashboard引用的已定义控制器。 这里要注意的是我使用控制器作为语法。这样我摆脱了我的脏范围

继续测试: directive.spec.js

    "use strict";
describe('ldaplogin.directive', function () {
    var $compile, $rootScope, $httpBackend, el, controller, scope, mockAuthenticationService
    var passPromise = false
    // Load the app module, which contains the directive        
    beforeEach(function () {
        module('app')
        /*
         * Create mocking services. We abstract our service to return a desired value to simulate it's real behaviour.
         * This leads us to be independant from the actual service and avoid data calls
         */
        module(function ($provide) {
            $provide.factory('AuthenticationService', ['$q',
                function ($q) {
                    var AuthenticationService = {
                        login: login
                    }
                    /*+
                     * 
                     * A spy is a stub. the and.callFake will execute the function when the spy is called
                     */
                    var login = jasmine.createSpy('login').and.callFake(function () {
                        var user = {
                            name: "j",
                            type: "hr"
                        }
                        if (passPromise) {
                            return $q.when(user);
                        } else {
                            return $q.reject("something went wrong");
                        }
                    });
                    return AuthenticationService;
                }]);
            $provide.service('util', function () {
                //mock util -- this is just an example
            });
        });

        inject(function (_$compile_, _$rootScope_, $injector, _AuthenticationService_) {
            // The injector unwraps the underscores (_) from around the parameter names when matching
            $compile = _$compile_;
            $rootScope = _$rootScope_;
            mockAuthenticationService = _AuthenticationService_
            $httpBackend = $injector.get('$httpBackend');
            //Get our directive
            el = angular.element("<my-ldap-login></my-ldap-login>")
            // Compile a piece of HTML containing the directive
            var t=$compile(el)($rootScope);
            // fire all the watches, so the scope expression will be evaluated
            $rootScope.$digest();
//            # Grab controller instance
//            This method can be used if the controller is isolated from the global scope. However for my directive I am using a defined controller so it can be fetched by injecting $controller
            var controller = el.controller("myLdapLogin")
            console.log(controller)
            //    # Grab scope. Depends on type of scope.
            //    # See angular.element documentation.
            scope = el.isolateScope() || el.scope()
        });
    });
    // Store references to $rootScope and $compile
    // so they are available to all tests in this describe block
    /*
     * Mock our controller
     * This syntax is with the controller as
     */
    beforeEach(inject(function ($controller) {
        controller = $controller('my-ldapLoginController', {
            AuthenticationService: mockAuthenticationService
        });
    }));
    it('Replaces the element with the appropriate content', function () {
        // Compile a piece of HTML containing the directive
        var element = $compile(el)($rootScope);
        // fire all the watches, so the scope expression will be evaluated
        $rootScope.$digest();
        // Check that the compiled element contains the templated content
        expect(element.html()).toContain("Uw gebruikersnaam of wachtwoord is verkeerd.");
    });
    it("should do something to the $rootScope", function () {
        //    # Grab scope. Depends on type of scope.
        //    # See angular.element documentation.
        console.log(scope)
        expect(scope).toBeDefined()
    })
    it("should resolve login", function () {
        /*We can reference the controller as teh variable vm to have the same syntax like we use in the controller (this equals var vm = this in the controller*/
        var vm = controller
        console.log(vm)
        var form = scope.loginForm
        console.log(form)
        form.username.$setViewValue('j');
        form.pass.$setViewValue('j');
        $rootScope.$digest()
        passPromise = true;
        var user
        vm.login(form).then(function (res) {
            var user = res;
        })
        $rootScope.$digest()
        expect(mockAuthenticationService.login).toHaveBeenCalled()
        expect(user).toEqual({name: "j", type: "hr"})
    })
});

问题出现在最后一次测试中:在范围内我有一个名为loginForm的对象。此loginForm包含表单的方法,但不包含我的命名输入。 这是我在测试中从作用域获得的loginForm对象:(注意;这是一个循环结构,我不能将JSON字符串化)

Id {$error: Object, $$success: Object, $pending: undefined, $name: "loginForm", $dirty: false…}
    $$parentForm:Object
    $$renameControl:(a,b)
    $$success:Object
    $addControl:(a)
    $commitViewValue:()
    $dirty:false
    $error:Object
    $invalid:false
    $name:"loginForm"
    $pending:undefined
    $pristine:true
    $removeControl:(a)
    $rollbackViewValue:()
    $setDirty:()
    $setPristine:()
    $setSubmitted:()
    $setUntouched:()
    $setValidity:(a,e,f)
    $submitted:false
    $valid:true
    __proto__:Object

如果我使用pre在我的html中记录我的loginForm,我会得到以下对象:

    {
  "$error": {
    "required": [
      {
        "$validators": {},
        "$asyncValidators": {},
        "$parsers": [],
        "$formatters": [
          null
        ],
        "$viewChangeListeners": [],
        "$untouched": true,
        "$touched": false,
        "$pristine": true,
        "$dirty": false,
        "$valid": false,
        "$invalid": true,
        "$error": {
          "required": true
        },
        "$name": "username",
        "$options": null
      },
      {
        "$validators": {},
        "$asyncValidators": {},
        "$parsers": [],
        "$formatters": [
          null
        ],
        "$viewChangeListeners": [],
        "$untouched": true,
        "$touched": false,
        "$pristine": true,
        "$dirty": false,
        "$valid": false,
        "$invalid": true,
        "$error": {
          "required": true
        },
        "$name": "pass",
        "$options": null
      }
    ]
  },
  "$name": "loginForm",
  "$dirty": false,
  "$pristine": true,
  "$valid": false,
  "$invalid": true,
  "$submitted": false,
  "username": {
    "$validators": {},
    "$asyncValidators": {},
    "$parsers": [],
    "$formatters": [
      null
    ],
    "$viewChangeListeners": [],
    "$untouched": true,
    "$touched": false,
    "$pristine": true,
    "$dirty": false,
    "$valid": false,
    "$invalid": true,
    "$error": {
      "required": true
    },
    "$name": "username",
    "$options": null
  },
  "pass": {
    "$validators": {},
    "$asyncValidators": {},
    "$parsers": [],
    "$formatters": [
      null
    ],
    "$viewChangeListeners": [],
    "$untouched": true,
    "$touched": false,
    "$pristine": true,
    "$dirty": false,
    "$valid": false,
    "$invalid": true,
    "$error": {
      "required": true
    },
    "$name": "pass",
    "$options": null
  }
}

在这个对象上,我将命名输入作为属性,但在我的测试范围内,在我的测试范围内,我没有。

有人知道如何将这些输入纳入我的范围内的表格吗?

我试图设置一个plunker但我似乎无法解决你可以使用ng-html2js修复的templateUrl问题

0 个答案:

没有答案