Testing AngularJS directives with external templates for unit testing

I have been utilizing the npm package karma-ng-html2js-preprocessor to achieve this task.

This is the content of my spec file:

describe('Directive: aGreatEye (external)', function() {
    var elm, scope;

    beforeEach(module('my-thing.template.html'));
    beforeEach(module('templates'));

    beforeEach(inject(function($rootScope, $compile) {

        elm = angular.element('<a-great-eye></a-great-eye>');
        scope = $rootScope;
        $compile(elm)(scope);
        scope.$digest();
    }));

    it('should execute first directive unit test correctly', function() {
        // add your assertions here
    });
});

However, I encountered an error message stating

Error: [$injector:modulerr] Failed to instantiate module my-thing.template.html due to:
    Error: [$injector:nomod] Module 'my-thing.template.html' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.

Even though the URL seems correct based on the basePath specified in the karma.config.js file.

Below is the content of my karma.config.js file:

module.exports = function (config) {

    // VIEW ONLINE AT: MAIN_ROOT/tests/jasmineReport/
    // You can either run via WebStorm or go online to do tests

    config.set({
        basePath: '.',
        frameworks: ['jasmine'],
        files: [
            // Vendor Files
            'bower_components/angular/angular.js',
            'bower_components/angular-mocks/angular-mocks.js',
            'bower_components/angular-ui-router/release/angular-ui-router.js',
            'bower_components/angular-filter/dist/angular-filter.js',
            'bower_components/angular-sanitize/angular-sanitize.js',
            'bower_components/lodash/lodash.min.js',

            // App-Specific Files
            'app/index.js',
            'app/common/models/*.js',
            'app/common/directives/*.js',
            'app/common/filters/*.js',
            'app/main/main.js',

            // App-Specific HTML Templates
            '*.html',
            '*.html.ext',

            // Test-Specific Files
            'tests/mock/**/*.js'
        ],
        exclude: [],
        preprocessors: {

            // HTML to JavaScript Preprocessors for
            // AngularJS Template Directives
            '*.html': ['ng-html2js']

        },
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['PhantomJS'],
        singleRun: true,
        concurrency: Infinity,

        // NgHTML 2 JS Pre-Processor - OPTIONS
        ngHtml2JsPreprocessor: {
            stripPrefix: 'public/',
            stripSuffix: '.ext',
            prependPrefix: 'served/',
            moduleName: "templates"
        }
    })
};

What am I missing here?

Answer №1

These are the tests I created to evaluate my directive, ensuring that the template is called and processed correctly:

'use strict';

/**
* @author Sophia Rodriguez
* 
* Test suite for menu widget directive
*/
describe('Menu Widget Directive Rendering', function () {

    beforeEach(module('myModule'));

    var compile, mockBackend, rootScope;

    // Step 1 - Inject the $compile service into the test
    beforeEach(inject(function ($compile, $httpBackend, $rootScope) {
        compile = $compile;
        mockBackend = $httpBackend;
        rootScope = $rootScope;
    }));


    it('should render HTML based on scope correctly', function () {
        var scope = rootScope.$new();

        mockBackend.expectGET('views/directives/menu.html').respond(
            '<div class="menu">' +
            '<ul>' +
            '<li><a href="#/" class="waves-effect waves-light">HOME</a></li>' +
            '<li><a href="#/activities" class="waves-effect waves-light">ACTIVITIES</a></li>' +
            '<li><a href="#/people" class="waves-effect waves-light">PEOPLE</a></li>' +
            '<li><a href="#/locations" class="waves-effect waves-light">LOCATIONS</a></li>' +
            '<div class="clear-both"></div>' +
            '</ul>' +
            '</div>');

        var element = compile('<menu></menu>')(scope);

        scope.$digest();
        mockBackend.flush();

        expect(element.html()).toEqual(
            '<div class="menu">' +
            '<ul>' +
            '<li><a href="#/" class="waves-effect waves-light">HOME</a></li>' +
            '<li><a href="#/activities" class="waves-effect waves-light">ACTIVITIES</a></li>' +
            '<li><a href="#/people" class="waves-effect waves-light">PEOPLE</a></li>' +
            '<li><a href="#/locations" class="waves-effect waves-light">LOCATIONS</a></li>' +
            '<div class="clear-both"></div>' +
            '</ul>' +
            '</div>'
        );
        mockBackend.verifyNoOutstandingExpectation();
    });

    it('should not render HTML if used not as element 1', function () {
        var scope = rootScope.$new();

        var element = compile('<div menu></div>')(scope);

        scope.$digest();

        expect(element.html()).toEqual('');
        mockBackend.verifyNoOutstandingRequest();
    });

    it('should not render HTML if used not as element 2', function () {
        var scope = rootScope.$new();

        var element = compile('<div class="menu"></div>')(scope);

        scope.$digest();

        expect(element.html()).toEqual('');
        mockBackend.verifyNoOutstandingRequest();
    });

    it('should not render HTML if used not as element 3', function () {
        var scope = rootScope.$new();

        var element = compile('<!-- directive: menu -->')(scope);

        scope.$digest();

        expect(element.html()).toBeUndefined();
        mockBackend.verifyNoOutstandingRequest();
    });
});

This test validates that the directive must be called using <menu></menu> to adhere to its restrictions

/**
* @author Sophia Rodriguez
*
* Menu directive to manage a menu based on the current $location.path
*/
angular.module('myModule')
    .directive('menu', ['Page', function (Page) {
        return {
            restrict: 'E',
            scope: true,
            templateUrl: 'views/directives/menu.html',
            link: function ($scope, $element, $attrs) {
                $scope.isCurrentPage = function (page) {
                    return Page.getActualPage() == page;
                };
            }
        };
}]);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Implementing useReducer in React for dynamic pagination changes

I'm currently working on a movie project and I am looking to implement pagination within the MoviesFromGenre component. Initially, the MoviesFromGenre component is rendered based on the id obtained from GenresList. I am eager to incorporate Pagination ...

Is there a way to trigger a function with ng-click in my AngularJS directive?

I am struggling to understand how to invoke a function from my directive using ng-click. Here is an example of my HTML: <div> <directive-name data="example"> <button class=btn btn-primary type="submit" ng-click="search()"> ...

I believe there may be a gap in the communication between TypeScript, JavaScript, Angular, Nginx, Alpine, and Docker within the network using Nginx. I am

After transitioning to Docker in order to create a virtual network to mimic a real network (using a bridge type with DNS that resolves the FQDN correctly to the corresponding IP), I encountered the following errors in the console.log - no data is being dis ...

To initiate animation upon a click event, follow these steps

Every time I try to access this webpage, it immediately sends me to a different page. This particular code is set up to redirect to specified links on its own. Is there a way for me to modify this code so that it only redirects when clicked? ...

Tips for creating a breakpoint in Sass specifically for a 414px iPhone screen size

For my latest project, I am utilizing sass and looking to incorporate a sass breakpoint or media query. My goal is to have the code execute only if the screen size is 414px or less. Below is my existing code: @include media-breakpoint-down(sm) { .sub-men ...

Accurate information typed into a text field

Similar Question: Validating Phone Numbers Using jQuery Regular Expressions I need to restrict input to only 10 digits in a text field, allowing , . ( ) - characters as well (following US phone number format). It should not accept an 11th digit when t ...

Setting background colors for classes based on an array

I have a total of six div elements on a single page, all sharing the same class. My goal is to give each one a unique color from an array I have prepared. I want to avoid any repetition of colors among these divs. Currently, I have managed to assign backg ...

The instance is referencing a property or method (...) that has not been defined, resulting in an error during rendering

"Unrecognized property or method "show" is being referenced in the rendering stage. To resolve this, ensure that the property is reactive by including it in the data option for functional components or initializing it for class-based components." This blo ...

The Vue.createApp function seems to be malfunctioning, whereas using the new Vue() method is functioning correctly

When running my code, I encountered the following error message: tesyya.js:16 Uncaught TypeError: Vue.createApp is not a function. My code snippet looks like this: const app = Vue.createApp({ data() { return { count: 4 } } }) const vm ...

Leveraging the power of Restangular by utilizing a complete URL

Restangular for AngularJS has a lot of useful functions, but one issue I have is the inability to easily pass a full URL to it. While I understand the advantages of using .one('something','someparam'), my problem lies in having to split ...

combining the use of $.ajax

Currently, I have multiple ajax calls scattered throughout my page and I am looking to streamline them into a single function. In various sections of my code, you will find functions like this: function AjaxCallOne () { //do something $.ajax({ type: ...

Disappearance of array data

I have been working on creating an array of objects with nested arrays, but I am facing an issue where data seems to go missing in the final step: const args_arr = []; const options_arr = []; let options = ''; let text = ""; for (let i = 0; ...

Run a function upon clicking a button in JavaScript

Could someone please help me figure out what I am doing incorrectly in the code below? I am attempting to trigger a function when a button is clicked. The HTML: <button id="btn1">Press me!</button> <input type="button" id="btn2" value="Pr ...

Error: ReactJs unable to find location

I'm attempting to update the status of the current page when it loads... const navigation = \[ { name: "Dashboard", href: "/dashboard", current: false }, { name: "Team", href: "/dashboard/team", current: fa ...

Tips for using JavaScript to style an array items individually

I have currently placed the entire array within a single div, but I would like to be able to display each element of the array separately so that I can style "date", "title", and "text" individually. This is my JSON structure: [ { "date": "Example ...

Inquiries about ngshow and the scope concept

I have a question about using AngularJS. I have multiple sections and only want to display one at a time using <section ng-show="section6_us"> </section> and <section ng-show="section7_us"> </section>. My scope has many variables. ...

Click on a link element within a container and create a pop-up window using jQuery

I'm having trouble creating a popup using jQuery for the second A tag within a div. Here's what I've attempted: $(function() { $(".sites").find("a").eq(1).css("color", "red").dialog({ autoOpen: false, show: { effect: "fade ...

Navigating through integration challenges between Angular UI Calendar and Google Calendar v3 API

Currently, I am in the process of integrating the Angular UI Calendar into my project using the Google Calendar API. I have successfully implemented it with the v3 Calendar API using the jQuery plugin version through FullCalendar on its own. However, I am ...

What troubleshooting steps should I take to address MQTT issues affecting the rendering of my website while using React and Raspberry Pi?

I am facing an issue where I cannot integrate mqtt with my react application on a Raspberry Pi 4. I am seeking assistance to resolve this problem. My setup includes: Distributor ID: Raspbian Description: Raspbian GNU/Linux 11 (bullseye) Release: 11 ...

Intermittently play a series of sound files, with only the final sound ringing out

My goal is to create an app that plays a sound based on a number input. I have several short MP3 audio files for different numbers, and I want the app to play these sounds in sequence. However, when I try to do this, only the last sound corresponding to th ...