describe('jqLite', function() { var scope, a, b, c; beforeEach(module(provideLog)); beforeEach(function() { a = jqLite('
A
')[0]; b = jqLite('
B
')[0]; c = jqLite('
C
')[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('
1
2'); 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
1
2'); 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(''); expect(nodes.length).toBe(1); expect(nodes[0].nodeType).toBe(8); }); it('should allow creation of script tags', function() { var nodes = jqLite(''); 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); }); }); describe('_data', function() { it('should provide access to the data present on the element', function() { var element = jqLite('foo'); var data = ['value']; element.data('val', data); expect(angular.element._data(element[0]).data.val).toBe(data); dealoc(element); }); it('should provide access to the events present on the element', function() { var element = jqLite('foo'); 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('foo'); 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(''); 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(''); 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(''), 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('
'), 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('foo'); 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('foo'); element.data('$isolateScope', scope); expect(element.isolateScope()).toBe(scope); dealoc(element); }); it('should retrieve scope attached to the html element if its 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(''); 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(''); 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('foo'); 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(''); 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('
'); 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('
'), 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 its 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('
'); 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('
'), 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 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('
x
'); 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('
x
'); 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('
text
'), 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('
text
'), 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;'); }); }); }); 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('').attr('multiple')).toBe('multiple'); expect(jqLite('').attr('multiple')).toBe('multiple'); }); it('should add/remove boolean attributes', function() { var select = jqLite(''); 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('
a
'); expect(elm.attr('non-existing')).toBeUndefined(); }); it('should return undefined for non-existing attributes on input', function() { var elm = jqLite(''); 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('
a
'); expect(elm.prop('className')).toBe('foo'); }); it('should set element property to a value', function() { var elm = jqLite('
a
'); 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(''); 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(''); 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('
'); 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('
ab
c'); 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'); }); }); describe('val', function() { it('should read, write value', function() { var input = jqLite(''); 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( '').val()).toEqual(['test 1', 'test 2']); expect(jqLite( '').val()).toEqual(['test 1']); expect(jqLite( '').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('
abc
'); 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('
abc
'); 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 }; 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 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('
root

parentchild

'); 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 msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1]); if (msie < 9){ var evnt = document.createEventObject(); evnt.srcElement = element; evnt.relatedTarget = relatedTarget; element.fireEvent('on' + type, evnt); return; }; var evnt = document.createEvent('MouseEvents'), 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('
').html('before-
after'); var div = root.find('div'); expect(div.replaceWith('span-bold-')).toEqual(div); expect(root.text()).toEqual('before-span-bold-after'); }); it('should replaceWith text', function() { var root = jqLite('
').html('before-
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('
before-
after-'); 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('
before-
after-
'); var contents = root.contents(); expect(contents.length).toEqual(5); expect(contents[0].data).toEqual(' some comment '); expect(contents[1].data).toEqual('before-'); }); // IE8 does not like this test, although the functionality may still work there. if (!msie || msie > 8) { it('should select all types iframe contents', function() { var iframe_ = document.createElement('iframe'), tested, iframe = jqLite(iframe_); function test() { 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(); tested = true; } iframe_.onload = iframe_.onreadystatechange = function() { if (iframe_.contentDocument) test(); }; iframe_.src = "/base/test/fixtures/iframe.html"; 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; }, 2000); }); } }); describe('append', function() { it('should append', function() { var root = jqLite('
'); expect(root.append('abc')).toEqual(root); expect(root.html().toLowerCase()).toEqual('abc'); }); it('should append text', function() { var root = jqLite('
'); 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('

foo

')).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('

some text node

').contents(); expect(root.append('

foo

')).toBe(root); expect(root.children().length).toBe(0); }); }); describe('wrap', function() { it('should wrap text node', function() { var root = jqLite('
A<a>B</a>C
'); var text = root.contents(); expect(text.wrap("")[0]).toBe(text[0]); expect(root.find('span').text()).toEqual('ABC'); }); it('should wrap free text node', function() { var root = jqLite('
A<a>B</a>C
'); var text = root.contents(); text.remove(); expect(root.text()).toBe(''); text.wrap(""); expect(text.parent().text()).toEqual('ABC'); }); }); describe('prepend', function() { it('should prepend to empty', function() { var root = jqLite('
'); expect(root.prepend('abc')).toEqual(root); expect(root.html().toLowerCase()).toEqual('abc'); }); it('should prepend to content', function() { var root = jqLite('
text
'); expect(root.prepend('abc')).toEqual(root); expect(root.html().toLowerCase()).toEqual('abctext'); }); it('should prepend text to content', function() { var root = jqLite('
text
'); 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('
'); expect(root.prepend([a, b, c])).toBe(root); expect(sortedHtml(root)). toBe('
A
B
C
'); }); it('should prepend array to content in the right order', function() { var root = jqLite('
text
'); expect(root.prepend([a, b, c])).toBe(root); expect(sortedHtml(root)). toBe('
A
B
C
text
'); }); }); describe('remove', function() { it('should remove', function() { var root = jqLite('
abc
'); var span = root.find('span'); expect(span.remove()).toEqual(span); expect(root.html()).toEqual(''); }); }); describe('after', function() { it('should after', function() { var root = jqLite('
'); var span = root.find('span'); expect(span.after('')).toEqual(span); expect(root.html().toLowerCase()).toEqual(''); }); it('should allow taking text', function() { var root = jqLite('
'); var span = root.find('span'); span.after('abc'); expect(root.html().toLowerCase()).toEqual('abc'); }); }); describe('parent', function() { it('should return parent or an empty set when no parent', function() { var parent = jqLite('

abc

'), 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('
abc
'); 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('

foo

'); 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('
bi
'); 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('
bTextNodei
'); 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('
text
'); 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('
aa
text
bb
'); var innerSpan = divs.find('span'); expect(innerSpan.length).toEqual(2); }); }); describe('eq', function() { it('should select the nth element ', function() { var element = jqLite('
aa
bb
'); 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('poke'), 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('poke'), 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('poke'), pokeSpy = jasmine.createSpy('poke'), data; element.on('click', pokeSpy); element.triggerHandler('click', [{hello: "world"}]); data = pokeSpy.mostRecentCall.args[1]; expect(data.hello).toBe("world"); }); }); 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'); }); }); });