0

Running a unit test fails with following:

Error: [$compile:multidir] Multiple directives [sendEmail, sendEmail] asking for new/isolated scope on: <send-email resolve="">

My unit test set up is the following. It tries to $compile the component twice in different tests. The it blocks are identical. But adding it a second time fails.

import angular from 'angular';
import 'angular-mocks';
import {sendEmail} from './send-email.js';

describe('component - sendEmail', () => {
    let $rootScope;
    let $compile;
    beforeEach(() => {
        angular
            .module('app')
            .component('sendEmail', sendEmail);
        angular.mock.module('app');

        inject(function(_$rootScope_, _$compile_) {
            $rootScope = _$rootScope_;
            $compile = _$compile_;
        });
    });

    it('...', () => {
        const element = $compile('<send-email resolve=""></send-email>')($rootScope);
        $rootScope.$digest();
        expect(element.find('.hp-send-email-container').length).toEqual(1);
    });

    // adding it will fail the test
    it('...', () => {
        const element = $compile('<send-email resolve=""></send-email>')($rootScope);
        $rootScope.$digest();
        expect(element.find('.hp-send-email-container').length).toEqual(1);
    });
});

So far I have tried reseting the scope and destroying the component and any combination of these. But it does not have any effect.

it('...', () => {
    // tried creating and using a new scope for $compile
    // const $scope = $rootScope.$new(true);
    const element = $compile('<send-email resolve=""></send-email>')($rootScope);
    $rootScope.$digest();
    expect(element.find('.hp-send-email-container').length).toEqual(1);

    // tried removing the element as well as destroying the scope
    // element.remove();
    // $scope.$destroy();
});

In the end what I want to achieve is to compile the component multiple times with different input and see the output. Maybe I am approaching the problem completely wrong. Any suggestions are welcome.

Kristjan Liiva
  • 9,069
  • 3
  • 25
  • 26

2 Answers2

1

The problem is that Angular modules are persistent. Existing module shouldn't be modified in specs.

There can be several directives with same name (selector), and component is a syntactic sugar for a directive (see this answer). This

beforeEach(() => {
    angular
        .module('app')
        .component('sendEmail', sendEmail);

results in adding a new directive to the stack for send-email selector with each test.

So this will result in $compile:multidir error on the second test, because components have isolated scopes, and there cannot be more than one directive per element with new scope.

Community
  • 1
  • 1
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
0

The answer from estus makes sense, but I also found an alternative solution. I haven't read it docs yet to know why it works, so if you know, leave a comment.

angular
   .module('app', [])
   .component('sendEmail', sendEmail);
angular.mock.module('app');

Only change is the added empty list of dependencies to the module. This allows $compile to be called in every it block.

Kristjan Liiva
  • 9,069
  • 3
  • 25
  • 26