Reputation: 480
I am trying to write a unit test for a component in Angular 1.5. I want to unit test that component and its dom nodes. This component contains a child component that is quite complex.
My goal is to unit test the outer component without compiling the child component.
Since I want to test the DOM as well, it is not sufficient to use $componentController for this test.
Here is a short example of what I would like to achieve:
Component code:
angular.module('app').component('myComponent', {
bindings: {
username: '<',
},
template: `
<span>{{ $ctrl.username }}</span>
<my-complex-component />
`
controller: function () {}
});
Unit test for my-component:
it('my-component should render username', function () {
var template = '<my-component username="username"></my-component>',
element,
scope,
date;
scope = $rootScope.$new();
scope.username = 'John';
element = $compile(template)(scope);
scope.$digest();
username = element.find('span');
expect(username.text()).to.be.equal('John');
});
my-complex-component should not be instantiated. It should resist in the template as it is. Creating the element in the unit test should result in
<span>John</span>
<my-complex-component />
Is there any way to do this?
Upvotes: 9
Views: 2138
Reputation: 106
You can do this by overriding your child component with $compileProvider.directive
even though it is a component, because a component is a special kind of directive.
In your unit tests register a new component with the compiler with the same name as your child component but do not provide a controller and set the template to an empty string. This will result in a component without any logic/complexity:
beforeEach(module('app', function($compileProvider){
$compileProvider.directive('myComplexComponent', function (){
return {
priority: 9999,
terminal: true,
template: ''
};
});
}));
So, the resulting html will be the most ideal for your outer component unit tests:
<span>John</span>
Upvotes: 3
Reputation: 1366
You could just overwrite the child components module definition in the unit test with an empty module.
// Unit Test Parent component
describe('Unit Testing: myComponent Component', () => {
beforeEach(() => {
// overwrite module
angular.module('myComplexComponent', []);
module('myComponent');
});
});
Now the parent component would still try to inject and render the child component, but as this child component is completely empty nothing will happen.
Upvotes: 0
Reputation: 112
There is a way to test it but the final result will be:
<span>John</span>
<ng-transclude></ng-transclude>
Component code:
angular.module('app').component('myComponent', {
enter code herebindings: {
username: '<',
},
template: `
<span>{{ $ctrl.username }}</span>
<ng-transclude></ng-transclude>
`,
transclude: true, // Added property
controller: function () {}
});
As you can see I deleted <my-complex-component>
, and added <ng-transclude>
instead.
That means that you can add your information from the outside and it will be injected at the location of ng-transclude.
For example this in your main html:
<my-component>
<my-complex-component></my-complex-component>
</my-component>
Will be in the DOM in the way that you wanted from the start:
<span>John</span>
<my-complex-component></my-complex-component>
After that your test should work.
I hope this answer is what you wanted and that it will help you.
Upvotes: 0
Reputation: 2352
Here is a very good explanation of how to test a component without rendering the whole tree under the current component.
What you want is called
shallow rendering (avoid rendering the whole template of the child component)
and Angular 1.5 doesn’t offer it out-of-the-box.
If you take a look at the link above, Wojciech Zawistowski shows how to use the helper below in order to achieve shallow rendering.
export function componentSpyOn(name) {
function componentSpy($provide) {
componentSpy.bindings = [];
$provide.decorator(name + 'Directive', ($delegate) => {
let component = $delegate[0];
component.template = '';
component.controller = class {
constructor() {
componentSpy.bindings.push(this);
}
};
return $delegate;
});
}
return componentSpy;
}
Upvotes: 3