'use strict';
describe('ngRef', function() {
beforeEach(function() {
jasmine.addMatchers({
toEqualJq: function(util) {
return {
compare: function(actual, expected) {
// Jquery <= 2.2 objects add a context property that is irrelevant for equality
if (actual && actual.hasOwnProperty('context')) {
delete actual.context;
}
if (expected && expected.hasOwnProperty('context')) {
delete expected.context;
}
return {
pass: util.equals(actual, expected)
};
}
};
}
});
});
describe('on a component', function() {
var myComponentController, attributeDirectiveController, $rootScope, $compile;
beforeEach(module(function($compileProvider) {
$compileProvider.component('myComponent', {
template: 'foo',
controller: function() {
myComponentController = this;
}
});
$compileProvider.directive('attributeDirective', function() {
return {
restrict: 'A',
controller: function() {
attributeDirectiveController = this;
}
};
});
}));
beforeEach(inject(function(_$compile_, _$rootScope_) {
$rootScope = _$rootScope_;
$compile = _$compile_;
}));
it('should bind in the current scope the controller of a component', function() {
$rootScope.$ctrl = 'undamaged';
$compile('')($rootScope);
expect($rootScope.$ctrl).toBe('undamaged');
expect($rootScope.myComponentRef).toBe(myComponentController);
});
it('should throw if the expression is not assignable', function() {
expect(function() {
$compile('')($rootScope);
}).toThrowMinErr('ngRef', 'nonassign', 'Expression in ngRef="\'hello\'" is non-assignable!');
});
it('should work with non:normalized entity name', function() {
$compile('')($rootScope);
expect($rootScope.myComponent1).toBe(myComponentController);
});
it('should work with data-non-normalized entity name', function() {
$compile('')($rootScope);
expect($rootScope.myComponent2).toBe(myComponentController);
});
it('should work with x-non-normalized entity name', function() {
$compile('')($rootScope);
expect($rootScope.myComponent3).toBe(myComponentController);
});
it('should work with data-non-normalized attribute name', function() {
$compile('')($rootScope);
expect($rootScope.myComponent1).toBe(myComponentController);
});
it('should work with x-non-normalized attribute name', function() {
$compile('')($rootScope);
expect($rootScope.myComponent2).toBe(myComponentController);
});
it('should not bind the controller of an attribute directive', function() {
$compile('')($rootScope);
expect($rootScope.myComponentRef).toBe(myComponentController);
});
it('should not leak to parent scopes', function() {
var template =
'
' +
'' +
'
';
$compile(template)($rootScope);
expect($rootScope.myComponent).toBe(undefined);
});
it('should nullify the variable once the component is destroyed', function() {
var template = '
';
var element = $compile(template)($rootScope);
expect($rootScope.myComponent).toBe(myComponentController);
var componentElement = element.children();
var isolateScope = componentElement.isolateScope();
componentElement.remove();
isolateScope.$destroy();
expect($rootScope.myComponent).toBe(null);
});
it('should be compatible with entering/leaving components', inject(function($animate) {
var template = '';
$rootScope.$ctrl = {};
var parent = $compile('')($rootScope);
var leaving = $compile(template)($rootScope);
var leavingController = myComponentController;
$animate.enter(leaving, parent);
expect($rootScope.myComponent).toBe(leavingController);
var entering = $compile(template)($rootScope);
var enteringController = myComponentController;
$animate.enter(entering, parent);
$animate.leave(leaving, parent);
expect($rootScope.myComponent).toBe(enteringController);
}));
it('should allow binding to a nested property', function() {
$rootScope.obj = {};
$compile('')($rootScope);
expect($rootScope.obj.myComponent).toBe(myComponentController);
});
});
it('should bind the jqlite wrapped DOM element if there is no component', inject(function($compile, $rootScope) {
var el = $compile('my text')($rootScope);
expect($rootScope.mySpan).toEqualJq(el);
expect($rootScope.mySpan[0].textContent).toBe('my text');
}));
it('should nullify the expression value if the DOM element is destroyed', inject(function($compile, $rootScope) {
var element = $compile('my text
')($rootScope);
element.children().remove();
expect($rootScope.mySpan).toBe(null);
}));
it('should bind the controller of an element directive', function() {
var myDirectiveController;
module(function($compileProvider) {
$compileProvider.directive('myDirective', function() {
return {
controller: function() {
myDirectiveController = this;
}
};
});
});
inject(function($compile, $rootScope) {
$compile('')($rootScope);
expect($rootScope.myDirective).toBe(myDirectiveController);
});
});
describe('ngRefRead', function() {
it('should bind the element instead of the controller of a component if ngRefRead="$element" is set', function() {
module(function($compileProvider) {
$compileProvider.component('myComponent', {
template: 'my text',
controller: function() {}
});
});
inject(function($compile, $rootScope) {
var el = $compile('')($rootScope);
expect($rootScope.myEl).toEqualJq(el);
expect($rootScope.myEl[0].textContent).toBe('my text');
});
});
it('should bind the element instead an element-directive controller if ngRefRead="$element" is set', function() {
module(function($compileProvider) {
$compileProvider.directive('myDirective', function() {
return {
restrict: 'E',
template: 'my text',
controller: function() {}
};
});
});
inject(function($compile, $rootScope) {
var el = $compile('')($rootScope);
expect($rootScope.myEl).toEqualJq(el);
expect($rootScope.myEl[0].textContent).toBe('my text');
});
});
it('should bind an attribute-directive controller if ngRefRead="controllerName" is set', function() {
var attrDirective1Controller;
module(function($compileProvider) {
$compileProvider.directive('elementDirective', function() {
return {
restrict: 'E',
template: 'my text',
controller: function() {}
};
});
$compileProvider.directive('attributeDirective1', function() {
return {
restrict: 'A',
controller: function() {
attrDirective1Controller = this;
}
};
});
$compileProvider.directive('attributeDirective2', function() {
return {
restrict: 'A',
controller: function() {}
};
});
});
inject(function($compile, $rootScope) {
var el = $compile('')($rootScope);
expect($rootScope.myController).toBe(attrDirective1Controller);
});
});
it('should throw if no controller is found for the ngRefRead value', function() {
module(function($compileProvider) {
$compileProvider.directive('elementDirective', function() {
return {
restrict: 'E',
template: 'my text',
controller: function() {}
};
});
});
inject(function($compile, $rootScope) {
expect(function() {
$compile('')($rootScope);
}).toThrowMinErr('ngRef', 'noctrl', 'The controller for ngRefRead="attribute" could not be found on ngRef="myController"');
});
});
});
it('should bind the jqlite element if the controller is on an attribute-directive', function() {
var myDirectiveController;
module(function($compileProvider) {
$compileProvider.directive('myDirective', function() {
return {
restrict: 'A',
template: 'my text',
controller: function() {
myDirectiveController = this;
}
};
});
});
inject(function($compile, $rootScope) {
var el = $compile('')($rootScope);
expect(myDirectiveController).toBeDefined();
expect($rootScope.myEl).toEqualJq(el);
expect($rootScope.myEl[0].textContent).toBe('my text');
});
});
it('should bind the jqlite element if the controller is on an class-directive', function() {
var myDirectiveController;
module(function($compileProvider) {
$compileProvider.directive('myDirective', function() {
return {
restrict: 'C',
template: 'my text',
controller: function() {
myDirectiveController = this;
}
};
});
});
inject(function($compile, $rootScope) {
var el = $compile('')($rootScope);
expect(myDirectiveController).toBeDefined();
expect($rootScope.myEl).toEqualJq(el);
expect($rootScope.myEl[0].textContent).toBe('my text');
});
});
describe('transclusion', function() {
it('should work with simple transclusion', function() {
module(function($compileProvider) {
$compileProvider
.component('myComponent', {
transclude: true,
template: '',
controller: function() {
this.text = 'SUCCESS';
}
});
});
inject(function($compile, $rootScope) {
var template = '{{myComponent.text}}';
var element = $compile(template)($rootScope);
$rootScope.$apply();
expect(element.text()).toBe('SUCCESS');
dealoc(element);
});
});
it('should be compatible with element transclude components', function() {
module(function($compileProvider) {
$compileProvider
.component('myComponent', {
transclude: 'element',
controller: function($animate, $element, $transclude) {
this.text = 'SUCCESS';
this.$postLink = function() {
$transclude(function(clone, newScope) {
$animate.enter(clone, $element.parent(), $element);
});
};
}
});
});
inject(function($compile, $rootScope) {
var template =
'' +
'' +
'{{myComponent.text}}' +
'' +
'
';
var element = $compile(template)($rootScope);
$rootScope.$apply();
expect(element.text()).toBe('SUCCESS');
dealoc(element);
});
});
it('should be compatible with ngIf and transclusion on same element', function() {
module(function($compileProvider) {
$compileProvider.component('myComponent', {
template: '',
transclude: true,
controller: function($scope) {
this.text = 'SUCCESS';
}
});
});
inject(function($compile, $rootScope) {
var template =
'' +
'' +
'{{myComponent.text}}' +
'' +
'
';
var element = $compile(template)($rootScope);
$rootScope.$apply('present = false');
expect(element.text()).toBe('');
$rootScope.$apply('present = true');
expect(element.text()).toBe('SUCCESS');
$rootScope.$apply('present = false');
expect(element.text()).toBe('');
$rootScope.$apply('present = true');
expect(element.text()).toBe('SUCCESS');
dealoc(element);
});
});
it('should be compatible with element transclude & destroy components', function() {
var myComponentController;
module(function($compileProvider) {
$compileProvider
.component('myTranscludingComponent', {
transclude: 'element',
controller: function($animate, $element, $transclude) {
myComponentController = this;
var currentClone, currentScope;
this.transclude = function(text) {
this.text = text;
$transclude(function(clone, newScope) {
currentClone = clone;
currentScope = newScope;
$animate.enter(clone, $element.parent(), $element);
});
};
this.destroy = function() {
currentClone.remove();
currentScope.$destroy();
};
}
});
});
inject(function($compile, $rootScope) {
var template =
'' +
'' +
'{{myComponent.text}}' +
'' +
'
';
var element = $compile(template)($rootScope);
$rootScope.$apply();
expect(element.text()).toBe('');
myComponentController.transclude('transcludedOk');
$rootScope.$apply();
expect(element.text()).toBe('transcludedOk');
myComponentController.destroy();
$rootScope.$apply();
expect(element.text()).toBe('');
});
});
it('should be compatible with element transclude directives', function() {
module(function($compileProvider) {
$compileProvider
.directive('myDirective', function($animate) {
return {
transclude: 'element',
controller: function() {
this.text = 'SUCCESS';
},
link: function(scope, element, attrs, ctrl, $transclude) {
$transclude(function(clone, newScope) {
$animate.enter(clone, element.parent(), element);
});
}
};
});
});
inject(function($compile, $rootScope) {
var template =
'' +
'' +
'{{myDirective.text}}' +
'' +
'
';
var element = $compile(template)($rootScope);
$rootScope.$apply();
expect(element.text()).toBe('SUCCESS');
dealoc(element);
});
});
});
it('should work with components with templates via $http', function() {
module(function($compileProvider) {
$compileProvider.component('httpComponent', {
templateUrl: 'template.html',
controller: function() {
this.me = true;
}
});
});
inject(function($compile, $httpBackend, $rootScope) {
var template = '
';
var element = $compile(template)($rootScope);
$httpBackend.expect('GET', 'template.html').respond('ok');
$rootScope.$apply();
expect($rootScope.controller).toBeUndefined();
$httpBackend.flush();
expect($rootScope.controller.me).toBe(true);
dealoc(element);
});
});
it('should work with ngRepeat-ed components', function() {
var controllers = [];
module(function($compileProvider) {
$compileProvider.component('myComponent', {
template: 'foo',
controller: function() {
controllers.push(this);
}
});
});
inject(function($compile, $rootScope) {
$rootScope.elements = [0,1,2,3,4];
$rootScope.controllers = []; // Initialize the array because ngRepeat creates a child scope
var template = '
';
var element = $compile(template)($rootScope);
$rootScope.$apply();
expect($rootScope.controllers).toEqual(controllers);
$rootScope.$apply('elements = []');
expect($rootScope.controllers).toEqual([null, null, null, null, null]);
});
});
});