Testing an Angular directive, like ng-if, through unit testing

My custom service and directive work together to remove DOM elements based on specific criteria.

The service checks a string parameter, while the directive removes the element it's attached to if the check fails.

This functionality is supported in IE11, Edge, Chrome, and FireFox. However, I'm struggling to replicate this behavior in my tests.

Check out the working code on JSBin

Here's an example of the service and its provider:

class PermissionService {
    constructor(permissions) {
        this.permissions = permissions;
    }

    addPermissions(permissions) {
        this.permissions = this.permissions.concat(permissions);
    }

    has(permission) {
        return this.permissions.indexOf(permission) !== -1;
    }
}

class PermissionProvider {
    constructor() {
        let _permissions = [];

        this.setPermissions = (permissions) => {
            _permissions = _permissions.concat(permissions);
        };

        this.$get = () => {
            return new PermissionService(_permissions);
        }
    }
}

Next, we have the directive implementation within an Angular module:

angular.module('PermissionTest', [])
  .directive('hasPermission', ['permission', '$compile', (service, $compile) => {
    return {
      restrict: 'A',
      priority: 1,
      terminal: true,
      compile: (element, attrs) => {
        let permission = attrs.hasPermission;

        if (service.has(permission)) {
            attrs.$set('hasPermission', null);
            return (scope, element) => {
                $compile(element)(scope);
            }
        } else {
            element.remove();
        }
      }
    }
  }])
  .provider('permission', PermissionProvider)
  .config(['permissionProvider', (provider) => {
    provider.setPermissions(['yes']);
  }]);

With this directive, removing elements from the DOM becomes straightforward:

<!-- Not removed -->
<div has-permission="yes">Is not removed</div>

<!-- Removed -->
<div has-permission="no">Is removed</div>

I've set up a test scenario that should pass but doesn't. I suspect the issue lies in the directive recompiling itself, even though it should happen during scope.$digest(). Any help with debugging would be appreciated.

let provider;
let service;

describe('directive: has-permission', () => {
    beforeEach(module('permissionChecker', (permissionProvider) => {
        provider = permissionProvider;
        provider.setPermissions(['crm.check.r', 'crm.check.c']);
    }));

    beforeEach(inject((permission) => {
        service = permission;
    }));
});

describe('if directive is not valid', () => {
    let scope;
    let element;

    beforeEach(inject(($rootScope, $compile) => {
        scope = $rootScope.$new();

        element = `<div>
            <div has-permission="crm.not.r"></div>
            <div has-permission="crm.check.r"></div>
        </div>`;

        element = $compile(element)(scope);

        scope.$digest();
    }));

    it('should remove one child element', () => {
        let test = angular.element(element).children();
        expect(test.length).toBe(1);
    });
});

Answer №1

After posting my question, I quickly found the solution to the problem that had been eluding me. It turned out that my assumption of being able to build the provider in a separate describe block was incorrect.

To debug this issue, I added an expectation to the first describe block:

describe('directive:has-permission', () => {
    // Provider setup mentioned in the question

    it('should exist', inject(($injector) => {
        expect($injector.has('hasPermissionDirective')).toBe(true);
    }));
});

This test passed without any issues.

However, when I added the same expectation to subsequent describe blocks, the test unexpectedly failed.

To resolve this, I decided to nest all the following describe blocks within the provider setup, which resolved the issue and all tests now pass as expected.

describe('directive:has-permission', () => {

    // Provider setup

    describe('if directive is not valid', () => {
        // Test setup and code
    });
});

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

Invoke a parent method from a nested child component in Vue

After setting up a project with vue-cli using the webpack template, I decided to incorporate a reusable bootstrap modal dialog in the App component. To achieve this, I created a method called showMessage in the App component that handles displaying the mod ...

Why does Vuetify/Javascript keep throwing a ReferenceError stating that the variable is undefined?

I'm currently developing in Vuetify and I want to incorporate a javascript client for Prometheus to fetch data for my application. You can find the page Here. Despite following their example, I keep encountering a ReferenceError: Prometheus is not def ...

The ng-repeat directive in the view is only updating when the entire array is fetched from the database

The issue of the angular directive ng-repeat not updating correctly appears to be quite common, with several existing threads discussing the problem. However, I have encountered a situation where it works in some cases but not in others, which is quite unu ...

Checking variable length time with JavaScript Regular Expression

My specific requirement is to validate a string ranging from 1 to 4 characters in length (where H stands for hour and M represents minutes): If the string has 1 character, it should be in the format of H (a digit between 0-9). A 2-character string should ...

Tips for updating information within a vue-component

I am working on a Vue component where I retrieve data from localStorage. Here is how I handle it: if (localStorage.getItem("user") !== null) { const obj_user = localStorage.getItem('user'); var user = JSON.parse(obj_user); } else { ...

Inconsistencies in Executing JavaScript Methods Between Mobile Safari and Android Chrome

I have a JavaScript method that appears like this: function onAction() { getValue1(); getValue2(); getValue3(); } When I invoke onAction(), I notice a discrepancy in behavior between Mobile Safari and Android Chrome. In Safari, all three meth ...

Serialization not being successful

Having an issue with my form that is being loaded and posted using ajax. When trying to send the data, nothing is added to the post. Here's a simplified version of the code: <form id="userForm"> <input type="text" name="username" /> ...

Error message when trying to get tree from Json using jqTree: "Oops! $(...).tree is not a valid function."

Currently, I am utilizing jqTree to display JSON data in a tree format. However, as I was implementing the demo of jqTree, an error occurred: "Uncaught TypeError: $(...).tree is not a function" ...

Efficiently select multiple classes in Material UI with just one target

I'm currently working with Material UI and I want to update the color of my icon class when the active class is triggered by react-router-dom's NavLink Here is my code: import React from "react"; import { makeStyles } from "@mater ...

Split a JavaScript string at a mathematical operator while ensuring that the operator is still included in

Hello there, I am attempting to break down a string of text into an array whenever a '+' or '-' is present. First of all, I am looking for a way to split at the plus sign and ensure it is included in the array. I have experimented with ...

es-lint is issuing a warning about the presence of multiple modules within the node_modules directory that have names differing only in their casing

After reviewing all my import statements, I found that everything looks correct. The only unusual import I have is react-bootstrap, which I import as: import { Jumbotron, Button } from 'react-bootstrap'; I'm using the Jumbotron and Button ...

The CSS selector for attribute ending with a specific value is not functioning as anticipated

I am facing an issue with selecting elements based on classes ending with either x-control-ui or y-control-ui: <div class="x-control-ui">x-control: <output id="x-control-output"></output></div> <input type=&q ...

When utilizing the Express framework, the object req.body is initially empty when collecting data from a basic

I'm encountering an issue where I receive an empty object in req.body when submitting my HTML form. This problem persists whether testing in Postman or directly submitting the form from localhost in the browser. Upon logging it in the node console, t ...

Reactjs implemented with Material UI and redux form framework, featuring a password toggle functionality without relying on hooks

Currently, I am working on a react project where I have developed a form framework that wraps Material-UI around Redux Form. If you want to check out the sandbox for this project, you can find it here: https://codesandbox.io/s/romantic-pasteur-nmw92 For ...

Collecting information from JSON files

Within the cells of my calendar are placeholders for events, dates, participants, and descriptions. Now, I am looking to populate these placeholders with data previously stored using localStorage. Unfortunately, my current code is not achieving this. How ...

Tips for transferring an entire array to a servlet and retrieving it efficiently

I have been attempting to collect all jqgrid data into one array and send it to a servlet. So far, I have tried the following code snippet: var rows= jQuery("#list").jqGrid('getRowData'); var paras=new Arr ...

My div does not refresh when using .load

I implemented ajax to retrieve the start time and end time from another page, and used setInterval to call this function every second like so. setInterval(function () { CheckTime() }, 1000); function CheckTime() { $.ajax({ url: "Get_TIme. ...

Tips for structuring dependencies within an Angular module

When developing my angular application, I implement the application component segmentation logic by organizing my folders in the following structure: app.module.js app.config.js components ---- core -------- core.module.js -------- etc... ---- component1 ...

Running a node.js project on the client side without using npm for deployment

Looking for a solution to efficiently deploy my nodejs project with frequent updates. The site does not have npm available, so I have to package the node_modules every time, which results in a large file size (80MB) that takes a long time to send via ftp t ...

Issue with Vue Composition API: Unable to append a property to an object

Currently, I am utilizing Vue's Composition API in my project. However, I have encountered an issue where the template renderer does not recognize changes when I add a property to a ref object. Below is the code snippet that demonstrates this problem: ...