Files
angular.js/test/jqLiteSpec.js
Michał Gołębiowski 9e7cb3c375 feat(jQuery): upgrade to jQuery to 2.1.1
The data jQuery method was re-implemented in 2.0 in a secure way. This made
current hacky Angular solution to move data between elements via changing the
value of the internal node[jQuery.expando] stop working. Instead, just copy the
data from the first element to the other one.

Testing cache leaks on jQuery 2.x is not possible in the same way as it's done
in jqLite or in jQuery 1.x as there is no publicly exposed data storage. One
way to test it would be to intercept all places where a jQuery object is created
to save a reference to the underlaying node but there is no single place in the
jQuery code through which all element creation passes (there are various
shortcuts for performance reasons). Instead we rely on jqLite.cache testing
to find potential data leaks.

BREAKING CHANGE: Angular no longer supports jQuery versions below 2.1.1.
2014-07-31 22:20:31 +02:00

1794 lines
56 KiB
JavaScript

'use strict';
describe('jqLite', function() {
var scope, a, b, c;
beforeEach(module(provideLog));
beforeEach(function() {
a = jqLite('<div>A</div>')[0];
b = jqLite('<div>B</div>')[0];
c = jqLite('<div>C</div>')[0];
});
beforeEach(inject(function($rootScope) {
scope = $rootScope;
this.addMatchers({
toJqEqual: function(expected) {
var msg = "Unequal length";
this.message = function() {return msg;};
var value = this.actual && expected && this.actual.length == expected.length;
for (var i = 0; value && i < expected.length; i++) {
var actual = jqLite(this.actual[i])[0];
var expect = jqLite(expected[i])[0];
value = value && equals(expect, actual);
msg = "Not equal at index: " + i
+ " - Expected: " + expect
+ " - Actual: " + actual;
}
return value;
}
});
}));
afterEach(function() {
dealoc(a);
dealoc(b);
dealoc(c);
});
it('should be jqLite when jqLiteMode is on, otherwise jQuery', function() {
expect(jqLite).toBe(_jqLiteMode ? JQLite : _jQuery);
});
describe('construction', function() {
it('should allow construction with text node', function() {
var text = a.firstChild;
var selected = jqLite(text);
expect(selected.length).toEqual(1);
expect(selected[0]).toEqual(text);
});
it('should allow construction with html', function() {
var nodes = jqLite('<div>1</div><span>2</span>');
expect(nodes[0].parentNode).toBeDefined();
expect(nodes[0].parentNode.nodeType).toBe(11); /** Document Fragment **/
expect(nodes[0].parentNode).toBe(nodes[1].parentNode);
expect(nodes.length).toEqual(2);
expect(nodes[0].innerHTML).toEqual('1');
expect(nodes[1].innerHTML).toEqual('2');
});
it('should allow construction of html with leading whitespace', function() {
var nodes = jqLite(' \n\r \r\n<div>1</div><span>2</span>');
expect(nodes[0].parentNode).toBeDefined();
expect(nodes[0].parentNode.nodeType).toBe(11); /** Document Fragment **/
expect(nodes[0].parentNode).toBe(nodes[1].parentNode);
expect(nodes.length).toBe(2);
expect(nodes[0].innerHTML).toBe('1');
expect(nodes[1].innerHTML).toBe('2');
});
it('should allow creation of comment tags', function() {
var nodes = jqLite('<!-- foo -->');
expect(nodes.length).toBe(1);
expect(nodes[0].nodeType).toBe(8);
});
it('should allow creation of script tags', function() {
var nodes = jqLite('<script></script>');
expect(nodes.length).toBe(1);
expect(nodes[0].tagName.toUpperCase()).toBe('SCRIPT');
});
it('should wrap document fragment', function() {
var fragment = jqLite(document.createDocumentFragment());
expect(fragment.length).toBe(1);
expect(fragment[0].nodeType).toBe(11);
});
it('should allow construction of <option> elements', function() {
var nodes = jqLite('<option>');
expect(nodes.length).toBe(1);
expect(nodes[0].nodeName.toLowerCase()).toBe('option');
});
// Special tests for the construction of elements which are restricted (in the HTML5 spec) to
// being children of specific nodes.
forEach([
'caption',
'colgroup',
'col',
'optgroup',
'opt',
'tbody',
'td',
'tfoot',
'th',
'thead',
'tr'
], function(name) {
it('should allow construction of <$NAME$> elements'.replace('$NAME$', name), function() {
var nodes = jqLite('<$NAME$>'.replace('$NAME$', name));
expect(nodes.length).toBe(1);
expect(nodes[0].nodeName.toLowerCase()).toBe(name);
});
});
});
describe('_data', function() {
it('should provide access to the events present on the element', function() {
var element = jqLite('<i>foo</i>');
expect(angular.element._data(element[0]).events).toBeUndefined();
element.on('click', function() { });
expect(angular.element._data(element[0]).events.click).toBeDefined();
});
});
describe('inheritedData', function() {
it('should retrieve data attached to the current element', function() {
var element = jqLite('<i>foo</i>');
element.data('myData', 'abc');
expect(element.inheritedData('myData')).toBe('abc');
dealoc(element);
});
it('should walk up the dom to find data', function() {
var element = jqLite('<ul><li><p><b>deep deep</b><p></li></ul>');
var deepChild = jqLite(element[0].getElementsByTagName('b')[0]);
element.data('myData', 'abc');
expect(deepChild.inheritedData('myData')).toBe('abc');
dealoc(element);
});
it('should return undefined when no data was found', function() {
var element = jqLite('<ul><li><p><b>deep deep</b><p></li></ul>');
var deepChild = jqLite(element[0].getElementsByTagName('b')[0]);
expect(deepChild.inheritedData('myData')).toBeFalsy();
dealoc(element);
});
it('should work with the child html element instead if the current element is the document obj',
function() {
var item = {},
doc = jqLite(document),
html = doc.find('html');
html.data('item', item);
expect(doc.inheritedData('item')).toBe(item);
expect(html.inheritedData('item')).toBe(item);
dealoc(doc);
}
);
it('should return null values', function () {
var ul = jqLite('<ul><li><p><b>deep deep</b><p></li></ul>'),
li = ul.find('li'),
b = li.find('b');
ul.data('foo', 'bar');
li.data('foo', null);
expect(b.inheritedData('foo')).toBe(null);
expect(li.inheritedData('foo')).toBe(null);
expect(ul.inheritedData('foo')).toBe('bar');
dealoc(ul);
});
it('should pass through DocumentFragment boundaries via host', function() {
var host = jqLite('<div></div>'),
frag = document.createDocumentFragment(),
$frag = jqLite(frag);
frag.host = host[0];
host.data("foo", 123);
host.append($frag);
expect($frag.inheritedData("foo")).toBe(123);
dealoc(host);
dealoc($frag);
});
});
describe('scope', function() {
it('should retrieve scope attached to the current element', function() {
var element = jqLite('<i>foo</i>');
element.data('$scope', scope);
expect(element.scope()).toBe(scope);
dealoc(element);
});
it('should retrieve isolate scope attached to the current element', function() {
var element = jqLite('<i>foo</i>');
element.data('$isolateScope', scope);
expect(element.isolateScope()).toBe(scope);
dealoc(element);
});
it('should retrieve scope attached to the html element if it\'s requested on the document',
function() {
var doc = jqLite(document),
html = doc.find('html'),
scope = {};
html.data('$scope', scope);
expect(doc.scope()).toBe(scope);
expect(html.scope()).toBe(scope);
dealoc(doc);
});
it('should walk up the dom to find scope', function() {
var element = jqLite('<ul><li><p><b>deep deep</b><p></li></ul>');
var deepChild = jqLite(element[0].getElementsByTagName('b')[0]);
element.data('$scope', scope);
expect(deepChild.scope()).toBe(scope);
dealoc(element);
});
it('should return undefined when no scope was found', function() {
var element = jqLite('<ul><li><p><b>deep deep</b><p></li></ul>');
var deepChild = jqLite(element[0].getElementsByTagName('b')[0]);
expect(deepChild.scope()).toBeFalsy();
dealoc(element);
});
});
describe('isolateScope', function() {
it('should retrieve isolate scope attached to the current element', function() {
var element = jqLite('<i>foo</i>');
element.data('$isolateScope', scope);
expect(element.isolateScope()).toBe(scope);
dealoc(element);
});
it('should not walk up the dom to find scope', function() {
var element = jqLite('<ul><li><p><b>deep deep</b><p></li></ul>');
var deepChild = jqLite(element[0].getElementsByTagName('b')[0]);
element.data('$isolateScope', scope);
expect(deepChild.isolateScope()).toBeUndefined();
dealoc(element);
});
it('should return undefined when no scope was found', function() {
var element = jqLite('<div></div>');
expect(element.isolateScope()).toBeFalsy();
dealoc(element);
});
});
describe('injector', function() {
it('should retrieve injector attached to the current element or its parent', function() {
var template = jqLite('<div><span></span></div>'),
span = template.children().eq(0),
injector = angular.bootstrap(template);
expect(span.injector()).toBe(injector);
dealoc(template);
});
it('should retrieve injector attached to the html element if it\'s requested on document',
function() {
var doc = jqLite(document),
html = doc.find('html'),
injector = {};
html.data('$injector', injector);
expect(doc.injector()).toBe(injector);
expect(html.injector()).toBe(injector);
dealoc(doc);
});
it('should do nothing with a noncompiled template', function() {
var template = jqLite('<div><span></span></div>');
expect(template.injector()).toBeUndefined();
dealoc(template);
});
});
describe('controller', function() {
it('should retrieve controller attached to the current element or its parent', function() {
var div = jqLite('<div><span></span></div>'),
span = div.find('span');
div.data('$ngControllerController', 'ngController');
span.data('$otherController', 'other');
expect(span.controller()).toBe('ngController');
expect(span.controller('ngController')).toBe('ngController');
expect(span.controller('other')).toBe('other');
expect(div.controller()).toBe('ngController');
expect(div.controller('ngController')).toBe('ngController');
expect(div.controller('other')).toBe(undefined);
dealoc(div);
});
});
describe('data', function() {
it('should set and get and remove data', function() {
var selected = jqLite([a, b, c]);
expect(selected.data('prop')).toBeUndefined();
expect(selected.data('prop', 'value')).toBe(selected);
expect(selected.data('prop')).toBe('value');
expect(jqLite(a).data('prop')).toBe('value');
expect(jqLite(b).data('prop')).toBe('value');
expect(jqLite(c).data('prop')).toBe('value');
jqLite(a).data('prop', 'new value');
expect(jqLite(a).data('prop')).toBe('new value');
expect(selected.data('prop')).toBe('new value');
expect(jqLite(b).data('prop')).toBe('value');
expect(jqLite(c).data('prop')).toBe('value');
expect(selected.removeData('prop')).toBe(selected);
expect(jqLite(a).data('prop')).toBeUndefined();
expect(jqLite(b).data('prop')).toBeUndefined();
expect(jqLite(c).data('prop')).toBeUndefined();
});
it('should only remove the specified value when providing a property name to removeData', function () {
var selected = jqLite(a);
expect(selected.data('prop1')).toBeUndefined();
selected.data('prop1', 'value');
selected.data('prop2', 'doublevalue');
expect(selected.data('prop1')).toBe('value');
expect(selected.data('prop2')).toBe('doublevalue');
selected.removeData('prop1');
expect(selected.data('prop1')).toBeUndefined();
expect(selected.data('prop2')).toBe('doublevalue');
selected.removeData('prop2');
});
it('should add and remove data on SVGs', function() {
var svg = jqLite('<svg><rect></rect></svg>');
svg.data('svg-level', 1);
expect(svg.data('svg-level')).toBe(1);
svg.children().data('rect-level', 2);
expect(svg.children().data('rect-level')).toBe(2);
svg.remove();
});
it('should not add to the cache if the node is a comment or text node', function() {
var calcCacheSize = function() {
var count = 0;
for (var k in jqLite.cache) { ++count; }
return count;
};
var nodes = jqLite('<!-- some comment --> and some text');
expect(calcCacheSize()).toEqual(0);
nodes.data('someKey');
expect(calcCacheSize()).toEqual(0);
nodes.data('someKey', 'someValue');
expect(calcCacheSize()).toEqual(0);
});
it('should provide the non-wrapped data calls', function() {
var node = document.createElement('div');
expect(jqLite.data(node, "foo")).toBeUndefined();
jqLite.data(node, "foo", "bar");
expect(jqLite.data(node, "foo")).toBe("bar");
expect(jqLite(node).data("foo")).toBe("bar");
expect(jqLite.data(node)).toBe(jqLite(node).data());
jqLite.removeData(node, "foo");
expect(jqLite.data(node, "foo")).toBeUndefined();
jqLite.data(node, "bar", "baz");
jqLite.removeData(node);
jqLite.removeData(node);
expect(jqLite.data(node, "bar")).toBeUndefined();
});
it('should emit $destroy event if element removed via remove()', function() {
var log = '';
var element = jqLite(a);
element.on('$destroy', function() {log+= 'destroy;';});
element.remove();
expect(log).toEqual('destroy;');
});
it('should emit $destroy event if an element is removed via html(\'\')', inject(function(log) {
var element = jqLite('<div><span>x</span></div>');
element.find('span').on('$destroy', log.fn('destroyed'));
element.html('');
expect(element.html()).toBe('');
expect(log).toEqual('destroyed');
}));
it('should emit $destroy event if an element is removed via empty()', inject(function(log) {
var element = jqLite('<div><span>x</span></div>');
element.find('span').on('$destroy', log.fn('destroyed'));
element.empty();
expect(element.html()).toBe('');
expect(log).toEqual('destroyed');
}));
it('should retrieve all data if called without params', function() {
var element = jqLite(a);
expect(element.data()).toEqual({});
element.data('foo', 'bar');
expect(element.data()).toEqual({foo: 'bar'});
element.data().baz = 'xxx';
expect(element.data()).toEqual({foo: 'bar', baz: 'xxx'});
});
it('should create a new data object if called without args', function() {
var element = jqLite(a),
data = element.data();
expect(data).toEqual({});
element.data('foo', 'bar');
expect(data).toEqual({foo: 'bar'});
});
it('should create a new data object if called with a single object arg', function() {
var element = jqLite(a),
newData = {foo: 'bar'};
element.data(newData);
expect(element.data()).toEqual({foo: 'bar'});
expect(element.data()).not.toBe(newData); // create a copy
});
it('should merge existing data object with a new one if called with a single object arg',
function() {
var element = jqLite(a);
element.data('existing', 'val');
expect(element.data()).toEqual({existing: 'val'});
var oldData = element.data(),
newData = {meLike: 'turtles', 'youLike': 'carrots'};
expect(element.data(newData)).toBe(element);
expect(element.data()).toEqual({meLike: 'turtles', youLike: 'carrots', existing: 'val'});
expect(element.data()).toBe(oldData); // merge into the old object
});
describe('data cleanup', function() {
it('should remove data on element removal', function() {
var div = jqLite('<div><span>text</span></div>'),
span = div.find('span');
span.data('name', 'angular');
span.remove();
expect(span.data('name')).toBeUndefined();
});
it('should remove event listeners on element removal', function() {
var div = jqLite('<div><span>text</span></div>'),
span = div.find('span'),
log = '';
span.on('click', function() { log += 'click;'; });
browserTrigger(span);
expect(log).toEqual('click;');
span.remove();
browserTrigger(span);
expect(log).toEqual('click;');
});
it('should work if the descendants of the element change while it\'s being removed', function() {
var div = jqLite('<div><p><span>text</span></p></div>');
div.find('p').on('$destroy', function() {
div.find('span').remove();
});
expect(function() {
div.remove();
}).not.toThrow();
});
});
});
describe('attr', function() {
it('should read write and remove attr', function() {
var selector = jqLite([a, b]);
expect(selector.attr('prop', 'value')).toEqual(selector);
expect(jqLite(a).attr('prop')).toEqual('value');
expect(jqLite(b).attr('prop')).toEqual('value');
expect(selector.attr({'prop': 'new value'})).toEqual(selector);
expect(jqLite(a).attr('prop')).toEqual('new value');
expect(jqLite(b).attr('prop')).toEqual('new value');
jqLite(b).attr({'prop': 'new value 2'});
expect(jqLite(selector).attr('prop')).toEqual('new value');
expect(jqLite(b).attr('prop')).toEqual('new value 2');
selector.removeAttr('prop');
expect(jqLite(a).attr('prop')).toBeFalsy();
expect(jqLite(b).attr('prop')).toBeFalsy();
});
it('should read boolean attributes as strings', function() {
var select = jqLite('<select>');
expect(select.attr('multiple')).toBeUndefined();
expect(jqLite('<select multiple>').attr('multiple')).toBe('multiple');
expect(jqLite('<select multiple="">').attr('multiple')).toBe('multiple');
expect(jqLite('<select multiple="x">').attr('multiple')).toBe('multiple');
});
it('should add/remove boolean attributes', function() {
var select = jqLite('<select>');
select.attr('multiple', false);
expect(select.attr('multiple')).toBeUndefined();
select.attr('multiple', true);
expect(select.attr('multiple')).toBe('multiple');
});
it('should normalize the case of boolean attributes', function() {
var input = jqLite('<input readonly>');
expect(input.attr('readonly')).toBe('readonly');
expect(input.attr('readOnly')).toBe('readonly');
expect(input.attr('READONLY')).toBe('readonly');
input.attr('readonly', false);
// attr('readonly') fails in jQuery 1.6.4, so we have to bypass it
//expect(input.attr('readOnly')).toBeUndefined();
//expect(input.attr('readonly')).toBeUndefined();
if (msie < 9) {
expect(input[0].getAttribute('readonly')).toBe('');
} else {
expect(input[0].getAttribute('readonly')).toBe(null);
}
//expect('readOnly' in input[0].attributes).toBe(false);
input.attr('readOnly', 'READonly');
expect(input.attr('readonly')).toBe('readonly');
expect(input.attr('readOnly')).toBe('readonly');
});
it('should return undefined for non-existing attributes', function() {
var elm = jqLite('<div class="any">a</div>');
expect(elm.attr('non-existing')).toBeUndefined();
});
it('should return undefined for non-existing attributes on input', function() {
var elm = jqLite('<input>');
expect(elm.attr('readonly')).toBeUndefined();
expect(elm.attr('readOnly')).toBeUndefined();
expect(elm.attr('disabled')).toBeUndefined();
});
});
describe('prop', function() {
it('should read element property', function() {
var elm = jqLite('<div class="foo">a</div>');
expect(elm.prop('className')).toBe('foo');
});
it('should set element property to a value', function() {
var elm = jqLite('<div class="foo">a</div>');
elm.prop('className', 'bar');
expect(elm[0].className).toBe('bar');
expect(elm.prop('className')).toBe('bar');
});
it('should set boolean element property', function() {
var elm = jqLite('<input type="checkbox">');
expect(elm.prop('checked')).toBe(false);
elm.prop('checked', true);
expect(elm.prop('checked')).toBe(true);
elm.prop('checked', '');
expect(elm.prop('checked')).toBe(false);
elm.prop('checked', 'lala');
expect(elm.prop('checked')).toBe(true);
elm.prop('checked', null);
expect(elm.prop('checked')).toBe(false);
});
});
describe('class', function() {
it('should properly do with SVG elements', function() {
// this is a jqLite & SVG only test (jquery doesn't behave this way right now, which is a bug)
if (!window.SVGElement || !_jqLiteMode) return;
var svg = jqLite('<svg><rect></rect></svg>');
var rect = svg.children();
expect(rect.hasClass('foo-class')).toBe(false);
rect.addClass('foo-class');
expect(rect.hasClass('foo-class')).toBe(true);
rect.removeClass('foo-class');
expect(rect.hasClass('foo-class')).toBe(false);
});
it('should ignore comment elements', function() {
var comment = jqLite(document.createComment('something'));
comment.addClass('whatever');
comment.hasClass('whatever');
comment.toggleClass('whatever');
comment.removeClass('whatever');
});
describe('hasClass', function() {
it('should check class', function() {
var selector = jqLite([a, b]);
expect(selector.hasClass('abc')).toEqual(false);
});
it('should make sure that partial class is not checked as a subset', function() {
var selector = jqLite([a, b]);
selector.addClass('a');
selector.addClass('b');
selector.addClass('c');
expect(selector.addClass('abc')).toEqual(selector);
expect(selector.removeClass('abc')).toEqual(selector);
expect(jqLite(a).hasClass('abc')).toEqual(false);
expect(jqLite(b).hasClass('abc')).toEqual(false);
expect(jqLite(a).hasClass('a')).toEqual(true);
expect(jqLite(a).hasClass('b')).toEqual(true);
expect(jqLite(a).hasClass('c')).toEqual(true);
});
});
describe('addClass', function() {
it('should allow adding of class', function() {
var selector = jqLite([a, b]);
expect(selector.addClass('abc')).toEqual(selector);
expect(jqLite(a).hasClass('abc')).toEqual(true);
expect(jqLite(b).hasClass('abc')).toEqual(true);
});
it('should ignore falsy values', function() {
var jqA = jqLite(a);
expect(jqA[0].className).toBe('');
jqA.addClass(undefined);
expect(jqA[0].className).toBe('');
jqA.addClass(null);
expect(jqA[0].className).toBe('');
jqA.addClass(false);
expect(jqA[0].className).toBe('');
});
it('should allow multiple classes to be added in a single string', function() {
var jqA = jqLite(a);
expect(a.className).toBe('');
jqA.addClass('foo bar baz');
expect(a.className).toBe('foo bar baz');
});
it('should not add duplicate classes', function() {
var jqA = jqLite(a);
expect(a.className).toBe('');
a.className = 'foo';
jqA.addClass('foo');
expect(a.className).toBe('foo');
jqA.addClass('bar foo baz');
expect(a.className).toBe('foo bar baz');
});
});
describe('toggleClass', function() {
it('should allow toggling of class', function() {
var selector = jqLite([a, b]);
expect(selector.toggleClass('abc')).toEqual(selector);
expect(jqLite(a).hasClass('abc')).toEqual(true);
expect(jqLite(b).hasClass('abc')).toEqual(true);
expect(selector.toggleClass('abc')).toEqual(selector);
expect(jqLite(a).hasClass('abc')).toEqual(false);
expect(jqLite(b).hasClass('abc')).toEqual(false);
expect(selector.toggleClass('abc'), true).toEqual(selector);
expect(jqLite(a).hasClass('abc')).toEqual(true);
expect(jqLite(b).hasClass('abc')).toEqual(true);
expect(selector.toggleClass('abc'), false).toEqual(selector);
expect(jqLite(a).hasClass('abc')).toEqual(false);
expect(jqLite(b).hasClass('abc')).toEqual(false);
});
it('should allow toggling multiple classes without a condition', function () {
var selector = jqLite([a, b]);
expect(selector.toggleClass('abc cde')).toBe(selector);
expect(jqLite(a).hasClass('abc')).toBe(true);
expect(jqLite(a).hasClass('cde')).toBe(true);
expect(jqLite(b).hasClass('abc')).toBe(true);
expect(jqLite(b).hasClass('cde')).toBe(true);
expect(selector.toggleClass('abc cde')).toBe(selector);
expect(jqLite(a).hasClass('abc')).toBe(false);
expect(jqLite(a).hasClass('cde')).toBe(false);
expect(jqLite(b).hasClass('abc')).toBe(false);
expect(jqLite(b).hasClass('cde')).toBe(false);
expect(selector.toggleClass('abc')).toBe(selector);
expect(selector.toggleClass('abc cde')).toBe(selector);
expect(jqLite(a).hasClass('abc')).toBe(false);
expect(jqLite(a).hasClass('cde')).toBe(true);
expect(jqLite(b).hasClass('abc')).toBe(false);
expect(jqLite(b).hasClass('cde')).toBe(true);
expect(selector.toggleClass('abc cde')).toBe(selector);
expect(jqLite(a).hasClass('abc')).toBe(true);
expect(jqLite(a).hasClass('cde')).toBe(false);
expect(jqLite(b).hasClass('abc')).toBe(true);
expect(jqLite(b).hasClass('cde')).toBe(false);
});
it('should allow toggling multiple classes with a condition', function () {
var selector = jqLite([a, b]);
selector.addClass('abc');
expect(selector.toggleClass('abc cde', true)).toBe(selector);
expect(jqLite(a).hasClass('abc')).toBe(true);
expect(jqLite(a).hasClass('cde')).toBe(true);
expect(jqLite(b).hasClass('abc')).toBe(true);
expect(jqLite(b).hasClass('cde')).toBe(true);
selector.removeClass('abc');
expect(selector.toggleClass('abc cde', false)).toBe(selector);
expect(jqLite(a).hasClass('abc')).toBe(false);
expect(jqLite(a).hasClass('cde')).toBe(false);
expect(jqLite(b).hasClass('abc')).toBe(false);
expect(jqLite(b).hasClass('cde')).toBe(false);
});
it('should not break for null / undefined selectors', function () {
var selector = jqLite([a, b]);
expect(selector.toggleClass(null)).toBe(selector);
expect(selector.toggleClass(undefined)).toBe(selector);
});
});
describe('removeClass', function() {
it('should allow removal of class', function() {
var selector = jqLite([a, b]);
expect(selector.addClass('abc')).toEqual(selector);
expect(selector.removeClass('abc')).toEqual(selector);
expect(jqLite(a).hasClass('abc')).toEqual(false);
expect(jqLite(b).hasClass('abc')).toEqual(false);
});
it('should correctly remove middle class', function() {
var element = jqLite('<div class="foo bar baz"></div>');
expect(element.hasClass('bar')).toBe(true);
element.removeClass('bar');
expect(element.hasClass('foo')).toBe(true);
expect(element.hasClass('bar')).toBe(false);
expect(element.hasClass('baz')).toBe(true);
});
it('should remove multiple classes specified as one string', function() {
var jqA = jqLite(a);
a.className = 'foo bar baz';
jqA.removeClass('foo baz noexistent');
expect(a.className).toBe('bar');
});
});
});
describe('css', function() {
it('should set and read css', function() {
var selector = jqLite([a, b]);
expect(selector.css('margin', '1px')).toEqual(selector);
expect(jqLite(a).css('margin')).toEqual('1px');
expect(jqLite(b).css('margin')).toEqual('1px');
expect(selector.css({'margin': '2px'})).toEqual(selector);
expect(jqLite(a).css('margin')).toEqual('2px');
expect(jqLite(b).css('margin')).toEqual('2px');
jqLite(b).css({'margin': '3px'});
expect(jqLite(selector).css('margin')).toEqual('2px');
expect(jqLite(a).css('margin')).toEqual('2px');
expect(jqLite(b).css('margin')).toEqual('3px');
selector.css('margin', '');
if (msie <= 8) {
expect(jqLite(a).css('margin')).toBe('auto');
expect(jqLite(b).css('margin')).toBe('auto');
} else {
expect(jqLite(a).css('margin')).toBeFalsy();
expect(jqLite(b).css('margin')).toBeFalsy();
}
});
it('should set a bunch of css properties specified via an object', function() {
if (msie <= 8) {
expect(jqLite(a).css('margin')).toBe('auto');
expect(jqLite(a).css('padding')).toBe('0px');
expect(jqLite(a).css('border')).toBeUndefined();
} else {
expect(jqLite(a).css('margin')).toBeFalsy();
expect(jqLite(a).css('padding')).toBeFalsy();
expect(jqLite(a).css('border')).toBeFalsy();
}
jqLite(a).css({'margin': '1px', 'padding': '2px', 'border': ''});
expect(jqLite(a).css('margin')).toBe('1px');
expect(jqLite(a).css('padding')).toBe('2px');
expect(jqLite(a).css('border')).toBeFalsy();
});
it('should correctly handle dash-separated and camelCased properties', function() {
var jqA = jqLite(a);
expect(jqA.css('z-index')).toBeOneOf('', 'auto');
expect(jqA.css('zIndex')).toBeOneOf('', 'auto');
jqA.css({'zIndex':5});
expect(jqA.css('z-index')).toBeOneOf('5', 5);
expect(jqA.css('zIndex')).toBeOneOf('5', 5);
jqA.css({'z-index':7});
expect(jqA.css('z-index')).toBeOneOf('7', 7);
expect(jqA.css('zIndex')).toBeOneOf('7', 7);
});
});
describe('text', function() {
it('should return null on empty', function() {
expect(jqLite().length).toEqual(0);
expect(jqLite().text()).toEqual('');
});
it('should read/write value', function() {
var element = jqLite('<div>ab</div><span>c</span>');
expect(element.length).toEqual(2);
expect(element[0].innerHTML).toEqual('ab');
expect(element[1].innerHTML).toEqual('c');
expect(element.text()).toEqual('abc');
expect(element.text('xyz') == element).toBeTruthy();
expect(element.text()).toEqual('xyzxyz');
});
it('should return text only for element or text nodes', function() {
expect(jqLite('<div>foo</div>').text()).toBe('foo');
expect(jqLite('<div>foo</div>').contents().eq(0).text()).toBe('foo');
expect(jqLite(document.createComment('foo')).text()).toBe('');
});
});
describe('val', function() {
it('should read, write value', function() {
var input = jqLite('<input type="text"/>');
expect(input.val('abc')).toEqual(input);
expect(input[0].value).toEqual('abc');
expect(input.val()).toEqual('abc');
});
it('should get an array of selected elements from a multi select', function () {
expect(jqLite(
'<select multiple>' +
'<option selected>test 1</option>' +
'<option selected>test 2</option>' +
'</select>').val()).toEqual(['test 1', 'test 2']);
expect(jqLite(
'<select multiple>' +
'<option selected>test 1</option>' +
'<option>test 2</option>' +
'</select>').val()).toEqual(['test 1']);
expect(jqLite(
'<select multiple>' +
'<option>test 1</option>' +
'<option>test 2</option>' +
'</select>').val()).toEqual(null);
});
});
describe('html', function() {
it('should return null on empty', function() {
expect(jqLite().length).toEqual(0);
expect(jqLite().html()).toEqual(null);
});
it('should read/write a value', function() {
var element = jqLite('<div>abc</div>');
expect(element.length).toEqual(1);
expect(element[0].innerHTML).toEqual('abc');
expect(element.html()).toEqual('abc');
expect(element.html('xyz') == element).toBeTruthy();
expect(element.html()).toEqual('xyz');
});
});
describe('empty', function() {
it('should write a value', function() {
var element = jqLite('<div>abc</div>');
expect(element.length).toEqual(1);
expect(element.empty() == element).toBeTruthy();
expect(element.html()).toEqual('');
});
});
describe('on', function() {
it('should bind to window on hashchange', function() {
if (jqLite.fn) return; // don't run in jQuery
var eventFn;
var window = {
document: {},
location: {},
alert: noop,
setInterval: noop,
length:10, // pretend you are an array
addEventListener: function(type, fn){
expect(type).toEqual('hashchange');
eventFn = fn;
},
removeEventListener: noop,
attachEvent: function(type, fn){
expect(type).toEqual('onhashchange');
eventFn = fn;
},
detachEvent: noop
};
window.window = window;
var log;
var jWindow = jqLite(window).on('hashchange', function() {
log = 'works!';
});
eventFn({type: 'hashchange'});
expect(log).toEqual('works!');
dealoc(jWindow);
});
it('should bind to all elements and return functions', function() {
var selected = jqLite([a, b]);
var log = '';
expect(selected.on('click', function() {
log += 'click on: ' + jqLite(this).text() + ';';
})).toEqual(selected);
browserTrigger(a, 'click');
expect(log).toEqual('click on: A;');
browserTrigger(b, 'click');
expect(log).toEqual('click on: A;click on: B;');
});
it('should not bind to comment or text nodes', function() {
var nodes = jqLite('<!-- some comment -->Some text');
var someEventHandler = jasmine.createSpy('someEventHandler');
nodes.on('someEvent', someEventHandler);
nodes.triggerHandler('someEvent');
expect(someEventHandler).not.toHaveBeenCalled();
});
it('should bind to all events separated by space', function() {
var elm = jqLite(a),
callback = jasmine.createSpy('callback');
elm.on('click keypress', callback);
elm.on('click', callback);
browserTrigger(a, 'click');
expect(callback).toHaveBeenCalled();
expect(callback.callCount).toBe(2);
callback.reset();
browserTrigger(a, 'keypress');
expect(callback).toHaveBeenCalled();
expect(callback.callCount).toBe(1);
});
it('should set event.target on IE', function() {
var elm = jqLite(a);
elm.on('click', function(event) {
expect(event.target).toBe(a);
});
browserTrigger(a, 'click');
});
it('should have event.isDefaultPrevented method', function() {
jqLite(a).on('click', function(e) {
expect(function() {
expect(e.isDefaultPrevented()).toBe(false);
e.preventDefault();
expect(e.isDefaultPrevented()).toBe(true);
}).not.toThrow();
});
browserTrigger(a, 'click');
});
describe('mouseenter-mouseleave', function() {
var root, parent, sibling, child, log;
beforeEach(function() {
log = '';
root = jqLite('<div>root<p>parent<span>child</span></p><ul></ul></div>');
parent = root.find('p');
sibling = root.find('ul');
child = parent.find('span');
parent.on('mouseenter', function() { log += 'parentEnter;'; });
parent.on('mouseleave', function() { log += 'parentLeave;'; });
child.on('mouseenter', function() { log += 'childEnter;'; });
child.on('mouseleave', function() { log += 'childLeave;'; });
});
afterEach(function() {
dealoc(root);
});
it('should fire mouseenter when coming from outside the browser window', function() {
if (window.jQuery) return;
var browserMoveTrigger = function(from, to){
var fireEvent = function(type, element, relatedTarget){
var evnt, msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1]);
if (msie < 9){
evnt = document.createEventObject();
evnt.srcElement = element;
evnt.relatedTarget = relatedTarget;
element.fireEvent('on' + type, evnt);
return;
}
evnt = document.createEvent('MouseEvents');
var originalPreventDefault = evnt.preventDefault,
appWindow = window,
fakeProcessDefault = true,
finalProcessDefault;
evnt.preventDefault = function() {
fakeProcessDefault = false;
return originalPreventDefault.apply(evnt, arguments);
};
var x = 0, y = 0;
evnt.initMouseEvent(type, true, true, window, 0, x, y, x, y, false, false,
false, false, 0, relatedTarget);
element.dispatchEvent(evnt);
};
fireEvent('mouseout', from[0], to[0]);
fireEvent('mouseover', to[0], from[0]);
};
browserMoveTrigger(root, parent);
expect(log).toEqual('parentEnter;');
browserMoveTrigger(parent, child);
expect(log).toEqual('parentEnter;childEnter;');
browserMoveTrigger(child, parent);
expect(log).toEqual('parentEnter;childEnter;childLeave;');
browserMoveTrigger(parent, root);
expect(log).toEqual('parentEnter;childEnter;childLeave;parentLeave;');
});
});
// Only run this test for jqLite and not normal jQuery
if ( _jqLiteMode ) {
it('should throw an error if eventData or a selector is passed', function() {
var elm = jqLite(a),
anObj = {},
aString = '',
aValue = 45,
callback = function() {};
expect(function() {
elm.on('click', anObj, callback);
}).toThrowMinErr('jqLite', 'onargs');
expect(function() {
elm.on('click', null, aString, callback);
}).toThrowMinErr('jqLite', 'onargs');
expect(function() {
elm.on('click', aValue, callback);
}).toThrowMinErr('jqLite', 'onargs');
});
}
});
describe('off', function() {
it('should do nothing when no listener was registered with bound', function() {
var aElem = jqLite(a);
aElem.off();
aElem.off('click');
aElem.off('click', function() {});
});
it('should do nothing when a specific listener was not registered', function () {
var aElem = jqLite(a);
aElem.on('click', function() {});
aElem.off('mouseenter', function() {});
});
it('should deregister all listeners', function() {
var aElem = jqLite(a),
clickSpy = jasmine.createSpy('click'),
mouseoverSpy = jasmine.createSpy('mouseover');
aElem.on('click', clickSpy);
aElem.on('mouseover', mouseoverSpy);
browserTrigger(a, 'click');
expect(clickSpy).toHaveBeenCalledOnce();
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).toHaveBeenCalledOnce();
clickSpy.reset();
mouseoverSpy.reset();
aElem.off();
browserTrigger(a, 'click');
expect(clickSpy).not.toHaveBeenCalled();
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).not.toHaveBeenCalled();
});
it('should deregister listeners for specific type', function() {
var aElem = jqLite(a),
clickSpy = jasmine.createSpy('click'),
mouseoverSpy = jasmine.createSpy('mouseover');
aElem.on('click', clickSpy);
aElem.on('mouseover', mouseoverSpy);
browserTrigger(a, 'click');
expect(clickSpy).toHaveBeenCalledOnce();
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).toHaveBeenCalledOnce();
clickSpy.reset();
mouseoverSpy.reset();
aElem.off('click');
browserTrigger(a, 'click');
expect(clickSpy).not.toHaveBeenCalled();
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).toHaveBeenCalledOnce();
mouseoverSpy.reset();
aElem.off('mouseover');
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).not.toHaveBeenCalled();
});
it('should deregister all listeners for types separated by spaces', function() {
var aElem = jqLite(a),
clickSpy = jasmine.createSpy('click'),
mouseoverSpy = jasmine.createSpy('mouseover');
aElem.on('click', clickSpy);
aElem.on('mouseover', mouseoverSpy);
browserTrigger(a, 'click');
expect(clickSpy).toHaveBeenCalledOnce();
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).toHaveBeenCalledOnce();
clickSpy.reset();
mouseoverSpy.reset();
aElem.off('click mouseover');
browserTrigger(a, 'click');
expect(clickSpy).not.toHaveBeenCalled();
browserTrigger(a, 'mouseover');
expect(mouseoverSpy).not.toHaveBeenCalled();
});
it('should deregister specific listener', function() {
var aElem = jqLite(a),
clickSpy1 = jasmine.createSpy('click1'),
clickSpy2 = jasmine.createSpy('click2');
aElem.on('click', clickSpy1);
aElem.on('click', clickSpy2);
browserTrigger(a, 'click');
expect(clickSpy1).toHaveBeenCalledOnce();
expect(clickSpy2).toHaveBeenCalledOnce();
clickSpy1.reset();
clickSpy2.reset();
aElem.off('click', clickSpy1);
browserTrigger(a, 'click');
expect(clickSpy1).not.toHaveBeenCalled();
expect(clickSpy2).toHaveBeenCalledOnce();
clickSpy2.reset();
aElem.off('click', clickSpy2);
browserTrigger(a, 'click');
expect(clickSpy2).not.toHaveBeenCalled();
});
it('should deregister specific listener within the listener and call subsequent listeners', function() {
var aElem = jqLite(a),
clickSpy = jasmine.createSpy('click'),
clickOnceSpy = jasmine.createSpy('clickOnce').andCallFake(function() {
aElem.off('click', clickOnceSpy);
});
aElem.on('click', clickOnceSpy);
aElem.on('click', clickSpy);
browserTrigger(a, 'click');
expect(clickOnceSpy).toHaveBeenCalledOnce();
expect(clickSpy).toHaveBeenCalledOnce();
browserTrigger(a, 'click');
expect(clickOnceSpy).toHaveBeenCalledOnce();
expect(clickSpy.callCount).toBe(2);
});
it('should deregister specific listener for multiple types separated by spaces', function() {
var aElem = jqLite(a),
masterSpy = jasmine.createSpy('master'),
extraSpy = jasmine.createSpy('extra');
aElem.on('click', masterSpy);
aElem.on('click', extraSpy);
aElem.on('mouseover', masterSpy);
browserTrigger(a, 'click');
browserTrigger(a, 'mouseover');
expect(masterSpy.callCount).toBe(2);
expect(extraSpy).toHaveBeenCalledOnce();
masterSpy.reset();
extraSpy.reset();
aElem.off('click mouseover', masterSpy);
browserTrigger(a, 'click');
browserTrigger(a, 'mouseover');
expect(masterSpy).not.toHaveBeenCalled();
expect(extraSpy).toHaveBeenCalledOnce();
});
// Only run this test for jqLite and not normal jQuery
if ( _jqLiteMode ) {
it('should throw an error if a selector is passed', function () {
var aElem = jqLite(a);
aElem.on('click', noop);
expect(function () {
aElem.off('click', noop, '.test');
}).toThrowMatching(/\[jqLite:offargs\]/);
});
}
});
describe('one', function() {
it('should only fire the callback once', function() {
var element = jqLite(a);
var spy = jasmine.createSpy('click');
element.one('click', spy);
browserTrigger(element, 'click');
expect(spy).toHaveBeenCalledOnce();
browserTrigger(element, 'click');
expect(spy).toHaveBeenCalledOnce();
});
it('should deregister when off is called', function() {
var element = jqLite(a);
var spy = jasmine.createSpy('click');
element.one('click', spy);
element.off('click', spy);
browserTrigger(element, 'click');
expect(spy).not.toHaveBeenCalled();
});
it('should return the same event object just as on() does', function() {
var element = jqLite(a);
var eventA, eventB;
element.on('click', function(event) {
eventA = event;
});
element.one('click', function(event) {
eventB = event;
});
browserTrigger(element, 'click');
expect(eventA).toEqual(eventB);
});
it('should not remove other event handlers of the same type after execution', function() {
var element = jqLite(a);
var calls = [];
element.one('click', function(event) {
calls.push('one');
});
element.on('click', function(event) {
calls.push('on');
});
browserTrigger(element, 'click');
browserTrigger(element, 'click');
expect(calls).toEqual(['one','on','on']);
});
});
describe('replaceWith', function() {
it('should replaceWith', function() {
var root = jqLite('<div>').html('before-<div></div>after');
var div = root.find('div');
expect(div.replaceWith('<span>span-</span><b>bold-</b>')).toEqual(div);
expect(root.text()).toEqual('before-span-bold-after');
});
it('should replaceWith text', function() {
var root = jqLite('<div>').html('before-<div></div>after');
var div = root.find('div');
expect(div.replaceWith('text-')).toEqual(div);
expect(root.text()).toEqual('before-text-after');
});
});
describe('children', function() {
it('should only select element nodes', function() {
var root = jqLite('<div><!-- some comment -->before-<div></div>after-<span></span>');
var div = root.find('div');
var span = root.find('span');
expect(root.children()).toJqEqual([div, span]);
});
});
describe('contents', function() {
it('should select all types child nodes', function() {
var root = jqLite('<div><!-- some comment -->before-<div></div>after-<span></span></div>');
var contents = root.contents();
expect(contents.length).toEqual(5);
expect(contents[0].data).toEqual(' some comment ');
expect(contents[1].data).toEqual('before-');
});
it('should select all types iframe contents', function() {
// IE8 does not like this test, although the functionality may still work there.
if (msie < 9) return;
var iframe_ = document.createElement('iframe');
var tested = false;
var iframe = jqLite(iframe_);
function test() {
var doc = iframe_.contentDocument || iframe_.contentWindow.document;
doc.body.innerHTML = '\n<span>Text</span>\n';
var contents = iframe.contents();
expect(contents[0]).toBeTruthy();
expect(contents.length).toBe(1);
expect(contents.prop('nodeType')).toBe(9);
expect(contents[0].body).toBeTruthy();
expect(jqLite(contents[0].body).contents().length).toBe(3);
iframe.remove();
doc = null;
tested = true;
}
iframe_.onload = iframe_.onreadystatechange = function() {
if (iframe_.contentDocument) test();
};
/* jshint scripturl:true */
iframe_.src = 'javascript:false';
jqLite(document).find('body').append(iframe);
// This test is potentially flaky on CI cloud instances, so there is a generous
// wait period...
waitsFor(function() { return tested; }, 'iframe to load', 5000);
});
});
describe('append', function() {
it('should append', function() {
var root = jqLite('<div>');
expect(root.append('<span>abc</span>')).toEqual(root);
expect(root.html().toLowerCase()).toEqual('<span>abc</span>');
});
it('should append text', function() {
var root = jqLite('<div>');
expect(root.append('text')).toEqual(root);
expect(root.html()).toEqual('text');
});
it('should append to document fragment', function() {
var root = jqLite(document.createDocumentFragment());
expect(root.append('<p>foo</p>')).toBe(root);
expect(root.children().length).toBe(1);
});
it('should not append anything if parent node is not of type element or docfrag', function() {
var root = jqLite('<p>some text node</p>').contents();
expect(root.append('<p>foo</p>')).toBe(root);
expect(root.children().length).toBe(0);
});
});
describe('wrap', function() {
it('should wrap text node', function() {
var root = jqLite('<div>A&lt;a&gt;B&lt;/a&gt;C</div>');
var text = root.contents();
expect(text.wrap("<span>")[0]).toBe(text[0]);
expect(root.find('span').text()).toEqual('A<a>B</a>C');
});
it('should wrap free text node', function() {
var root = jqLite('<div>A&lt;a&gt;B&lt;/a&gt;C</div>');
var text = root.contents();
text.remove();
expect(root.text()).toBe('');
text.wrap("<span>");
expect(text.parent().text()).toEqual('A<a>B</a>C');
});
});
describe('prepend', function() {
it('should prepend to empty', function() {
var root = jqLite('<div>');
expect(root.prepend('<span>abc</span>')).toEqual(root);
expect(root.html().toLowerCase()).toEqual('<span>abc</span>');
});
it('should prepend to content', function() {
var root = jqLite('<div>text</div>');
expect(root.prepend('<span>abc</span>')).toEqual(root);
expect(root.html().toLowerCase()).toEqual('<span>abc</span>text');
});
it('should prepend text to content', function() {
var root = jqLite('<div>text</div>');
expect(root.prepend('abc')).toEqual(root);
expect(root.html().toLowerCase()).toEqual('abctext');
});
it('should prepend array to empty in the right order', function() {
var root = jqLite('<div>');
expect(root.prepend([a, b, c])).toBe(root);
expect(sortedHtml(root)).
toBe('<div><div>A</div><div>B</div><div>C</div></div>');
});
it('should prepend array to content in the right order', function() {
var root = jqLite('<div>text</div>');
expect(root.prepend([a, b, c])).toBe(root);
expect(sortedHtml(root)).
toBe('<div><div>A</div><div>B</div><div>C</div>text</div>');
});
});
describe('remove', function() {
it('should remove', function() {
var root = jqLite('<div><span>abc</span></div>');
var span = root.find('span');
expect(span.remove()).toEqual(span);
expect(root.html()).toEqual('');
});
});
describe('after', function() {
it('should after', function() {
var root = jqLite('<div><span></span></div>');
var span = root.find('span');
expect(span.after('<i></i><b></b>')).toEqual(span);
expect(root.html().toLowerCase()).toEqual('<span></span><i></i><b></b>');
});
it('should allow taking text', function() {
var root = jqLite('<div><span></span></div>');
var span = root.find('span');
span.after('abc');
expect(root.html().toLowerCase()).toEqual('<span></span>abc');
});
});
describe('parent', function() {
it('should return parent or an empty set when no parent', function() {
var parent = jqLite('<div><p>abc</p></div>'),
child = parent.find('p');
expect(parent.parent()).toBeTruthy();
expect(parent.parent().length).toEqual(0);
expect(child.parent().length).toBe(1);
expect(child.parent()[0]).toBe(parent[0]);
});
it('should return empty set when no parent', function() {
var element = jqLite('<div>abc</div>');
expect(element.parent()).toBeTruthy();
expect(element.parent().length).toEqual(0);
});
it('should return empty jqLite object when parent is a document fragment', function() {
//this is quite unfortunate but jQuery 1.5.1 behaves this way
var fragment = document.createDocumentFragment(),
child = jqLite('<p>foo</p>');
fragment.appendChild(child[0]);
expect(child[0].parentNode).toBe(fragment);
expect(child.parent().length).toBe(0);
});
});
describe('next', function() {
it('should return next sibling', function() {
var element = jqLite('<div><b>b</b><i>i</i></div>');
var b = element.find('b');
var i = element.find('i');
expect(b.next()).toJqEqual([i]);
});
it('should ignore non-element siblings', function() {
var element = jqLite('<div><b>b</b>TextNode<!-- comment node --><i>i</i></div>');
var b = element.find('b');
var i = element.find('i');
expect(b.next()).toJqEqual([i]);
});
});
describe('find', function() {
it('should find child by name', function() {
var root = jqLite('<div><div>text</div></div>');
var innerDiv = root.find('div');
expect(innerDiv.length).toEqual(1);
expect(innerDiv.html()).toEqual('text');
});
it('should find child by name and not care about text nodes', function() {
var divs = jqLite('<div><span>aa</span></div>text<div><span>bb</span></div>');
var innerSpan = divs.find('span');
expect(innerSpan.length).toEqual(2);
});
});
describe('eq', function() {
it('should select the nth element ', function() {
var element = jqLite('<div><span>aa</span></div><div><span>bb</span></div>');
expect(element.find('span').eq(0).html()).toBe('aa');
expect(element.find('span').eq(-1).html()).toBe('bb');
expect(element.find('span').eq(20).length).toBe(0);
});
});
describe('triggerHandler', function() {
it('should trigger all registered handlers for an event', function() {
var element = jqLite('<span>poke</span>'),
pokeSpy = jasmine.createSpy('poke'),
clickSpy1 = jasmine.createSpy('clickSpy1'),
clickSpy2 = jasmine.createSpy('clickSpy2');
element.on('poke', pokeSpy);
element.on('click', clickSpy1);
element.on('click', clickSpy2);
expect(pokeSpy).not.toHaveBeenCalled();
expect(clickSpy1).not.toHaveBeenCalled();
expect(clickSpy2).not.toHaveBeenCalled();
element.triggerHandler('poke');
expect(pokeSpy).toHaveBeenCalledOnce();
expect(clickSpy1).not.toHaveBeenCalled();
expect(clickSpy2).not.toHaveBeenCalled();
element.triggerHandler('click');
expect(clickSpy1).toHaveBeenCalledOnce();
expect(clickSpy2).toHaveBeenCalledOnce();
});
it('should pass in a dummy event', function() {
// we need the event to have at least preventDefault because angular will call it on
// all anchors with no href automatically
var element = jqLite('<a>poke</a>'),
pokeSpy = jasmine.createSpy('poke'),
event;
element.on('click', pokeSpy);
element.triggerHandler('click');
event = pokeSpy.mostRecentCall.args[0];
expect(event.preventDefault).toBeDefined();
});
it('should pass data as an additional argument', function() {
var element = jqLite('<a>poke</a>'),
pokeSpy = jasmine.createSpy('poke'),
data;
element.on('click', pokeSpy);
element.triggerHandler('click', [{hello: "world"}]);
data = pokeSpy.mostRecentCall.args[1];
expect(data.hello).toBe("world");
});
it('should mark event as prevented if preventDefault is called', function() {
var element = jqLite('<a>poke</a>'),
pokeSpy = jasmine.createSpy('poke'),
event;
element.on('click', pokeSpy);
element.triggerHandler('click');
event = pokeSpy.mostRecentCall.args[0];
expect(event.isDefaultPrevented()).toBe(false);
event.preventDefault();
expect(event.isDefaultPrevented()).toBe(true);
});
it('should support handlers that deregister themselves', function() {
var element = jqLite('<a>poke</a>'),
clickSpy = jasmine.createSpy('click'),
clickOnceSpy = jasmine.createSpy('clickOnce').andCallFake(function() {
element.off('click', clickOnceSpy);
});
element.on('click', clickOnceSpy);
element.on('click', clickSpy);
element.triggerHandler('click');
expect(clickOnceSpy).toHaveBeenCalledOnce();
expect(clickSpy).toHaveBeenCalledOnce();
element.triggerHandler('click');
expect(clickOnceSpy).toHaveBeenCalledOnce();
expect(clickSpy.callCount).toBe(2);
});
});
describe('camelCase', function() {
it('should leave non-dashed strings alone', function() {
expect(camelCase('foo')).toBe('foo');
expect(camelCase('')).toBe('');
expect(camelCase('fooBar')).toBe('fooBar');
});
it('should covert dash-separated strings to camelCase', function() {
expect(camelCase('foo-bar')).toBe('fooBar');
expect(camelCase('foo-bar-baz')).toBe('fooBarBaz');
expect(camelCase('foo:bar_baz')).toBe('fooBarBaz');
});
it('should covert browser specific css properties', function() {
expect(camelCase('-moz-foo-bar')).toBe('MozFooBar');
expect(camelCase('-webkit-foo-bar')).toBe('webkitFooBar');
expect(camelCase('-webkit-foo-bar')).toBe('webkitFooBar');
});
});
});