UNPKG

angular-engine.js

Version:

Using AngularJS with the Closure Compiler =========================================

784 lines (603 loc) 25.6 kB
'use strict'; describe('ngMessages', function() { beforeEach(inject.strictDi()); beforeEach(module('ngMessages')); function messageChildren(element) { return (element.length ? element[0] : element).querySelectorAll('[ng-message], [ng-message-exp]'); } function s(str) { return str.replace(/\s+/g,''); } var element; afterEach(function() { dealoc(element); }); it('should render based off of a hashmap collection', inject(function($rootScope, $compile) { element = $compile('<div ng-messages="col">' + ' <div ng-message="val">Message is set</div>' + '</div>')($rootScope); $rootScope.$digest(); expect(element.text()).not.toContain('Message is set'); $rootScope.$apply(function() { $rootScope.col = { val: true }; }); expect(element.text()).toContain('Message is set'); })); it('should render the same message if multiple message keys match', inject(function($rootScope, $compile) { element = $compile('<div ng-messages="col">' + ' <div ng-message="one, two, three">Message is set</div>' + '</div>')($rootScope); $rootScope.$digest(); expect(element.text()).not.toContain('Message is set'); $rootScope.$apply(function() { $rootScope.col = { one: true }; }); expect(element.text()).toContain('Message is set'); $rootScope.$apply(function() { $rootScope.col = { two: true, one: false }; }); expect(element.text()).toContain('Message is set'); $rootScope.$apply(function() { $rootScope.col = { three: true, two: false }; }); expect(element.text()).toContain('Message is set'); $rootScope.$apply(function() { $rootScope.col = { three: false }; }); expect(element.text()).not.toContain('Message is set'); })); it('should use the when attribute when an element directive is used', inject(function($rootScope, $compile) { element = $compile('<ng-messages for="col">' + ' <ng-message when="val">Message is set</div>' + '</ng-messages>')($rootScope); $rootScope.$digest(); expect(element.text()).not.toContain('Message is set'); $rootScope.$apply(function() { $rootScope.col = { val: true }; }); expect(element.text()).toContain('Message is set'); })); it('should render the same message if multiple message keys match based on the when attribute', inject(function($rootScope, $compile) { element = $compile('<ng-messages for="col">' + ' <ng-message when=" one two three ">Message is set</div>' + '</ng-messages>')($rootScope); $rootScope.$digest(); expect(element.text()).not.toContain('Message is set'); $rootScope.$apply(function() { $rootScope.col = { one: true }; }); expect(element.text()).toContain('Message is set'); $rootScope.$apply(function() { $rootScope.col = { two: true, one: false }; }); expect(element.text()).toContain('Message is set'); $rootScope.$apply(function() { $rootScope.col = { three: true, two: false }; }); expect(element.text()).toContain('Message is set'); $rootScope.$apply(function() { $rootScope.col = { three: false }; }); expect(element.text()).not.toContain('Message is set'); })); it('should allow a dynamic expression to be set when ng-message-exp is used', inject(function($rootScope, $compile) { element = $compile('<div ng-messages="col">' + ' <div ng-message-exp="variable">Message is crazy</div>' + '</div>')($rootScope); $rootScope.$digest(); expect(element.text()).not.toContain('Message is crazy'); $rootScope.$apply(function() { $rootScope.variable = 'error'; $rootScope.col = { error: true }; }); expect(element.text()).toContain('Message is crazy'); $rootScope.$apply(function() { $rootScope.col = { error: false, failure: true }; }); expect(element.text()).not.toContain('Message is crazy'); $rootScope.$apply(function() { $rootScope.variable = ['failure']; }); expect(element.text()).toContain('Message is crazy'); $rootScope.$apply(function() { $rootScope.variable = null; }); expect(element.text()).not.toContain('Message is crazy'); })); it('should allow a dynamic expression to be set when the when-exp attribute is used', inject(function($rootScope, $compile) { element = $compile('<ng-messages for="col">' + ' <ng-message when-exp="variable">Message is crazy</ng-message>' + '</ng-messages>')($rootScope); $rootScope.$digest(); expect(element.text()).not.toContain('Message is crazy'); $rootScope.$apply(function() { $rootScope.variable = 'error, failure'; $rootScope.col = { error: true }; }); expect(element.text()).toContain('Message is crazy'); $rootScope.$apply(function() { $rootScope.col = { error: false, failure: true }; }); expect(element.text()).toContain('Message is crazy'); $rootScope.$apply(function() { $rootScope.variable = []; }); expect(element.text()).not.toContain('Message is crazy'); $rootScope.$apply(function() { $rootScope.variable = null; }); expect(element.text()).not.toContain('Message is crazy'); })); they('should render empty when $prop is used as a collection value', { 'null': null, 'false': false, '0': 0, '[]': [], '[{}]': [{}], '': '', '{ val2 : true }': { val2: true } }, function(prop) { inject(function($rootScope, $compile) { element = $compile('<div ng-messages="col">' + ' <div ng-message="val">Message is set</div>' + '</div>')($rootScope); $rootScope.$digest(); $rootScope.$apply(function() { $rootScope.col = prop; }); expect(element.text()).not.toContain('Message is set'); }); }); they('should insert and remove matching inner elements when $prop is used as a value', { 'true': true, '1': 1, '{}': {}, '[]': [], '[null]': [null] }, function(prop) { inject(function($rootScope, $compile) { element = $compile('<div ng-messages="col">' + ' <div ng-message="blue">This message is blue</div>' + ' <div ng-message="red">This message is red</div>' + '</div>')($rootScope); $rootScope.$apply(function() { $rootScope.col = {}; }); expect(messageChildren(element).length).toBe(0); expect(trim(element.text())).toEqual(''); $rootScope.$apply(function() { $rootScope.col = { blue: true, red: false }; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual('This message is blue'); $rootScope.$apply(function() { $rootScope.col = { red: prop }; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual('This message is red'); $rootScope.$apply(function() { $rootScope.col = null; }); expect(messageChildren(element).length).toBe(0); expect(trim(element.text())).toEqual(''); $rootScope.$apply(function() { $rootScope.col = { blue: 0, red: null }; }); expect(messageChildren(element).length).toBe(0); expect(trim(element.text())).toEqual(''); }); }); it('should display the elements in the order defined in the DOM', inject(function($rootScope, $compile) { element = $compile('<div ng-messages="col">' + ' <div ng-message="one">Message#one</div>' + ' <div ng-message="two">Message#two</div>' + ' <div ng-message="three">Message#three</div>' + '</div>')($rootScope); $rootScope.$apply(function() { $rootScope.col = { three: true, one: true, two: true }; }); angular.forEach(['one','two','three'], function(key) { expect(s(element.text())).toEqual('Message#' + key); $rootScope.$apply(function() { $rootScope.col[key] = false; }); }); expect(s(element.text())).toEqual(''); })); it('should add ng-active/ng-inactive CSS classes to the element when errors are/aren\'t displayed', inject(function($rootScope, $compile) { element = $compile('<div ng-messages="col">' + ' <div ng-message="ready">This message is ready</div>' + '</div>')($rootScope); $rootScope.$apply(function() { $rootScope.col = {}; }); expect(element.hasClass('ng-active')).toBe(false); expect(element.hasClass('ng-inactive')).toBe(true); $rootScope.$apply(function() { $rootScope.col = { ready: true }; }); expect(element.hasClass('ng-active')).toBe(true); expect(element.hasClass('ng-inactive')).toBe(false); })); it('should automatically re-render the messages when other directives dynmically change them', inject(function($rootScope, $compile) { element = $compile('<div ng-messages="col">' + ' <div ng-message="primary">Enter something</div>' + ' <div ng-repeat="item in items">' + ' <div ng-message-exp="item.name">{{ item.text }}</div>' + ' </div>' + '</div>')($rootScope); $rootScope.$apply(function() { $rootScope.col = {}; $rootScope.items = [ { text: 'Your age is incorrect', name: 'age' }, { text: 'You\'re too tall man!', name: 'height' }, { text: 'Your hair is too long', name: 'hair' } ]; }); expect(messageChildren(element).length).toBe(0); expect(trim(element.text())).toEqual(""); $rootScope.$apply(function() { $rootScope.col = { hair: true }; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("Your hair is too long"); $rootScope.$apply(function() { $rootScope.col = { age: true, hair: true}; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("Your age is incorrect"); $rootScope.$apply(function() { // remove the age! $rootScope.items.shift(); }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("Your hair is too long"); $rootScope.$apply(function() { // remove the hair! $rootScope.items.length = 0; $rootScope.col.primary = true; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("Enter something"); })); it('should be compatible with ngBind', inject(function($rootScope, $compile) { element = $compile('<div ng-messages="col">' + ' <div ng-message="required" ng-bind="errorMessages.required"></div>' + ' <div ng-message="extra" ng-bind="errorMessages.extra"></div>' + '</div>')($rootScope); $rootScope.$apply(function() { $rootScope.col = { required: true, extra: true }; $rootScope.errorMessages = { required: 'Fill in the text field.', extra: 'Extra error message.' }; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual('Fill in the text field.'); $rootScope.$apply(function() { $rootScope.col.required = false; $rootScope.col.extra = true; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual('Extra error message.'); $rootScope.$apply(function() { $rootScope.errorMessages.extra = 'New error message.'; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual('New error message.'); })); // issue #12856 it('should only detach the message object that is associated with the message node being removed', inject(function($rootScope, $compile, $animate) { // We are going to spy on the `leave` method to give us control over // when the element is actually removed spyOn($animate, 'leave'); // Create a basic ng-messages set up element = $compile('<div ng-messages="col">' + ' <div ng-message="primary">Enter something</div>' + '</div>')($rootScope); // Trigger the message to be displayed $rootScope.col = { primary: true }; $rootScope.$digest(); expect(messageChildren(element).length).toEqual(1); var oldMessageNode = messageChildren(element)[0]; // Remove the message $rootScope.col = { primary: undefined }; $rootScope.$digest(); // Since we have spied on the `leave` method, the message node is still in the DOM expect($animate.leave).toHaveBeenCalledOnce(); var nodeToRemove = $animate.leave.mostRecentCall.args[0][0]; expect(nodeToRemove).toBe(oldMessageNode); $animate.leave.reset(); // Add the message back in $rootScope.col = { primary: true }; $rootScope.$digest(); // Simulate the animation completing on the node jqLite(nodeToRemove).remove(); // We should not get another call to `leave` expect($animate.leave).not.toHaveBeenCalled(); // There should only be the new message node expect(messageChildren(element).length).toEqual(1); var newMessageNode = messageChildren(element)[0]; expect(newMessageNode).not.toBe(oldMessageNode); })); it('should render animations when the active/inactive classes are added/removed', function() { module('ngAnimate'); module('ngAnimateMock'); inject(function($rootScope, $compile, $animate) { element = $compile('<div ng-messages="col">' + ' <div ng-message="ready">This message is ready</div>' + '</div>')($rootScope); $rootScope.$apply(function() { $rootScope.col = {}; }); var event = $animate.queue.pop(); expect(event.event).toBe('setClass'); expect(event.args[1]).toBe('ng-inactive'); expect(event.args[2]).toBe('ng-active'); $rootScope.$apply(function() { $rootScope.col = { ready: true }; }); event = $animate.queue.pop(); expect(event.event).toBe('setClass'); expect(event.args[1]).toBe('ng-active'); expect(event.args[2]).toBe('ng-inactive'); }); }); describe('when including templates', function() { they('should work with a dynamic collection model which is managed by ngRepeat', {'<div ng-messages-include="...">': '<div ng-messages="item">' + '<div ng-messages-include="abc.html"></div>' + '</div>', '<ng-messages-include src="...">': '<ng-messages for="item">' + '<ng-messages-include src="abc.html"></ng-messages-include>' + '</ng-messages>'}, function(html) { inject(function($compile, $rootScope, $templateCache) { $templateCache.put('abc.html', '<div ng-message="a">A</div>' + '<div ng-message="b">B</div>' + '<div ng-message="c">C</div>'); html = '<div><div ng-repeat="item in items">' + html + '</div></div>'; $rootScope.items = [{},{},{}]; element = $compile(html)($rootScope); $rootScope.$apply(function() { $rootScope.items[0].a = true; $rootScope.items[1].b = true; $rootScope.items[2].c = true; }); var elements = element[0].querySelectorAll('[ng-repeat]'); // all three collections should have at least one error showing up expect(messageChildren(element).length).toBe(3); expect(messageChildren(elements[0]).length).toBe(1); expect(messageChildren(elements[1]).length).toBe(1); expect(messageChildren(elements[2]).length).toBe(1); // this is the standard order of the displayed error messages expect(element.text().trim()).toBe('ABC'); $rootScope.$apply(function() { $rootScope.items[0].a = false; $rootScope.items[0].c = true; $rootScope.items[1].b = false; $rootScope.items[2].c = false; $rootScope.items[2].a = true; }); // with the 2nd item gone and the values changed // we should see both 1 and 3 changed expect(element.text().trim()).toBe('CA'); $rootScope.$apply(function() { // add the value for the 2nd item back $rootScope.items[1].b = true; $rootScope.items.reverse(); }); // when reversed we get back to our original value expect(element.text().trim()).toBe('ABC'); }); }); they('should remove the $prop element and place a comment anchor node where it used to be', {'<div ng-messages-include="...">': '<div ng-messages="data">' + '<div ng-messages-include="abc.html"></div>' + '</div>', '<ng-messages-include src="...">': '<ng-messages for="data">' + '<ng-messages-include src="abc.html"></ng-messages-include>' + '</ng-messages>'}, function(html) { inject(function($compile, $rootScope, $templateCache) { $templateCache.put('abc.html', '<div></div>'); element = $compile(html)($rootScope); $rootScope.$digest(); var includeElement = element[0].querySelector('[ng-messages-include], ng-messages-include'); expect(includeElement).toBeFalsy(); var comment = element[0].childNodes[0]; expect(comment.nodeType).toBe(8); expect(comment.nodeValue).toBe(' ngMessagesInclude: abc.html '); }); }); they('should load a remote template using $prop', {'<div ng-messages-include="...">': '<div ng-messages="data">' + '<div ng-messages-include="abc.html"></div>' + '</div>', '<ng-messages-include src="...">': '<ng-messages for="data">' + '<ng-messages-include src="abc.html"></ng-messages-include>' + '</ng-messages>'}, function(html) { inject(function($compile, $rootScope, $templateCache) { $templateCache.put('abc.html', '<div ng-message="a">A</div>' + '<div ng-message="b">B</div>' + '<div ng-message="c">C</div>'); element = $compile(html)($rootScope); $rootScope.$apply(function() { $rootScope.data = { 'a': 1, 'b': 2, 'c': 3 }; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("A"); $rootScope.$apply(function() { $rootScope.data = { 'c': 3 }; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("C"); }); }); it('should cache the template after download', inject(function($rootScope, $compile, $templateCache, $httpBackend) { $httpBackend.expect('GET', 'tpl').respond(201, '<div>abc</div>'); expect($templateCache.get('tpl')).toBeUndefined(); element = $compile('<div ng-messages="data"><div ng-messages-include="tpl"></div></div>')($rootScope); $rootScope.$digest(); $httpBackend.flush(); expect($templateCache.get('tpl')).toBeDefined(); })); it('should re-render the messages after download without an extra digest', inject(function($rootScope, $compile, $httpBackend) { $httpBackend.expect('GET', 'my-messages').respond(201, '<div ng-message="required">You did not enter a value</div>'); element = $compile('<div ng-messages="data">' + ' <div ng-messages-include="my-messages"></div>' + ' <div ng-message="failed">Your value is that of failure</div>' + '</div>')($rootScope); $rootScope.data = { required: true, failed: true }; $rootScope.$digest(); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("Your value is that of failure"); $httpBackend.flush(); $rootScope.$digest(); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("You did not enter a value"); })); it('should allow for overriding the remote template messages within the element depending on where the remote template is placed', inject(function($compile, $rootScope, $templateCache) { $templateCache.put('abc.html', '<div ng-message="a">A</div>' + '<div ng-message="b">B</div>' + '<div ng-message="c">C</div>'); element = $compile('<div ng-messages="data">' + ' <div ng-message="a">AAA</div>' + ' <div ng-messages-include="abc.html"></div>' + ' <div ng-message="c">CCC</div>' + '</div>')($rootScope); $rootScope.$apply(function() { $rootScope.data = { 'a': 1, 'b': 2, 'c': 3 }; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("AAA"); $rootScope.$apply(function() { $rootScope.data = { 'b': 2, 'c': 3 }; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("B"); $rootScope.$apply(function() { $rootScope.data = { 'c': 3 }; }); expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual("C"); })); }); describe('when multiple', function() { they('should show all truthy messages when the $prop attr is present', { 'multiple': 'multiple', 'ng-messages-multiple': 'ng-messages-multiple' }, function(prop) { inject(function($rootScope, $compile) { element = $compile('<div ng-messages="data" ' + prop + '>' + ' <div ng-message="one">1</div>' + ' <div ng-message="two">2</div>' + ' <div ng-message="three">3</div>' + '</div>')($rootScope); $rootScope.$apply(function() { $rootScope.data = { 'one': true, 'two': false, 'three': true }; }); expect(messageChildren(element).length).toBe(2); expect(s(element.text())).toContain("13"); }); }); it('should render all truthy messages from a remote template', inject(function($rootScope, $compile, $templateCache) { $templateCache.put('xyz.html', '<div ng-message="x">X</div>' + '<div ng-message="y">Y</div>' + '<div ng-message="z">Z</div>'); element = $compile('<div ng-messages="data" ng-messages-multiple="true">' + '<div ng-messages-include="xyz.html"></div>' + '</div>')($rootScope); $rootScope.$apply(function() { $rootScope.data = { 'x': 'a', 'y': null, 'z': true }; }); expect(messageChildren(element).length).toBe(2); expect(s(element.text())).toEqual("XZ"); $rootScope.$apply(function() { $rootScope.data.y = {}; }); expect(messageChildren(element).length).toBe(3); expect(s(element.text())).toEqual("XYZ"); })); it('should render and override all truthy messages from a remote template', inject(function($rootScope, $compile, $templateCache) { $templateCache.put('xyz.html', '<div ng-message="x">X</div>' + '<div ng-message="y">Y</div>' + '<div ng-message="z">Z</div>'); element = $compile('<div ng-messages="data" ng-messages-multiple="true">' + '<div ng-message="y">YYY</div>' + '<div ng-message="z">ZZZ</div>' + '<div ng-messages-include="xyz.html"></div>' + '</div>')($rootScope); $rootScope.$apply(function() { $rootScope.data = { 'x': 'a', 'y': null, 'z': true }; }); expect(messageChildren(element).length).toBe(2); expect(s(element.text())).toEqual("ZZZX"); $rootScope.$apply(function() { $rootScope.data.y = {}; }); expect(messageChildren(element).length).toBe(3); expect(s(element.text())).toEqual("YYYZZZX"); })); }); });