2

I'm trying to test a directive that allows me to do infinite scroll in my app. The directive works fine in my production code.

This question is really close to How to test AngularJS Directive with scrolling and if someone says they are identical questions, I'll delete this one. However, it is not solving my problem (at least I cannot figure out how).

Here's my directive:

(function(){
    "use strict";
    angular.module('moduleA').directive('whenScrolled',Directive);

    Directive.$inject = [];

    function Directive(){
        return {
            restrict : 'A', // Restrict to Attributes
            link : postLink
        };

        // Bind the element's scroll event
        function postLink(scope,elem,attr){
            var raw = elem[0];
            elem.bind('scroll',eventMethod);

            function eventMethod(){
                if ( raw.scrollTop + raw.offsetHeight >= raw.scrollHeight ) {
                    scope.$apply(attr.whenScrolled);
                }
            };
        };
    };

})();

And here's my Karma test:

describe('whenScrolled Directive:',function(){

    beforeEach(module('moduleA'));

    // Create a list in HTML and force overflow for a scroll event
    var html = 
    '<div id="test" ' + 
    '     style="max-height:30px;overflow-y:scroll;" ' +
    '     when-scrolled="testScroll()">' + 
    '    <ul>' + 
    '        <li>Testing</li>' +
    '        <li>Testing</li>' +
    '        <li>Testing</li>' +
    '        <li>Testing</li>' +
    '        <li>Testing</li>' +
    '        <li>Testing</li>' +
    '        <li>Testing</li>' +
    '        <li>Testing</li>' +
    '        <li>Testing</li>' +
    '        <li>Testing</li>' +
    '    </ul>' + 
    '</div>';

    var $rootScope,
        element;

    beforeEach(inject([
        '$compile','$rootScope',
        function($compile,$rs){
            $rootScope = $rs;
            $rootScope.isScrolled = false;
            $rootScope.testScroll = function(){
                $rootScope.isScrolled = true;
            };

            element = $compile(html)($rootScope);
            $rootScope.$digest();
        }
]));

    it('should activate on scroll',function(){
        expect(element).toBeDefined();
        $rootScope.$broadcast('scroll'); <-- does nothing? -->
        expect($rootScope.isScrolled).toBe(true); <-- fails -->
    });

});

My karma.conf.js:

// Created May 04, 2016
module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',


    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],


    // list of files / patterns to load in the browser
    files: [
      'vendor/angular/angular.js',
        'vendor/angular/angular-mocks.js', 
        'moduleA/moduleA.view.html',
        'moduleA/moduleA.module.js',
        'moduleA/moduleA.controller.js',
        'moduleB/moduleB.view.html',
        'moduleB/moduleB.module.js',
        'moduleB/moduleB.controller.js',
        'test/module-A-tests.js',
        'test/module-B-tests.js'
    ],


    // list of files to exclude
    exclude: [
      'vendor/bootstrap*',
      'vendor/jquery*',
    ],


    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-    preprocessor
    preprocessors: {
        // karma-ng-html2js-preprocessor for templates in directives
        'moduleA/moduleA.form.view.html': 'ng-html2js',
        'moduleB/moduleB.view.html': 'ng-html2js'
    },


    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],


    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}

My thought was that if I have $rootScope broadcast 'scroll' out to all of it's children (in my case, $scope), and $scope was tied to my element with my directive, then that would trigger the method I provided (doScrollFn), and $scope.isScrolled would change.

But as far as I can tell, the doScrollFn never gets triggered.

Community
  • 1
  • 1
westandy
  • 1,360
  • 2
  • 16
  • 41

1 Answers1

3

I'm still not 100% sure what I did wrong, but after googling karma-ng-html-preprocessor, I found this:

https://www.npmjs.com/package/karma-ng-html2js-preprocessor-with-templates which lead me to:

https://github.com/vojtajina/ng-directive-testing which lead me to:

https://angularjs.org/#create-components and mainly, this:

https://github.com/vojtajina/ng-directive-testing/blob/start/test/tabsSpec.js.

And here's my test that works:

describe('whenScrolled Directive',function(){

    beforeEach(module('moduleA'));

    var html = 
        '<div id="test" ' +
        '       style="min-height:5px;max-height:30px;overflow-y:scroll;" ' +
        '     when-scrolled="testScroll()">' +
        '   <ul>' +
        '       <li>Testing</li>' +
        '       <li>Testing</li>' +
        '       <li>Testing</li>' +
        '       <li>Testing</li>' +
        '       <li>Testing</li>' +
        '       <li>Testing</li>' +
        '       <li>Testing</li>' +
        '       <li>Testing</li>' +
        '       <li>Testing</li>' +
        '       <li>Testing</li>' +
        '   </ul>' + 
        '</div>';

    var $rootScope,
        element;

    beforeEach(function(){
        inject([
            '$compile','$rootScope',
            function($compile,$rs){
                $rootScope = $rs;
                $rootScope.isScrolled = false;
                $rootScope.testScroll = function(){
                    $rootScope.isScrolled = true;
                };

                element = angular.element(html);
                $compile(element)($rootScope);
                $rootScope.$digest();
            }
    ]);
    });

    it('should activate on scroll',function(){
        expect(element.find('li').length).toBe(10);
        element.triggerHandler('scroll');
        expect($rootScope.isScrolled).toBe(true);
    });

});

If this saves anybody some time and trouble, then it was worth the write-up.

westandy
  • 1,360
  • 2
  • 16
  • 41