UNPKG

extjs-gpl

Version:

GPL licensed version of Sencha Ext JS

1,239 lines (1,018 loc) 157 kB
/* global expect, xdescribe, Ext, jasmine, spyOn */ (function() { function makeObservableSuite(isMixin) { describe(isMixin ? "Ext.mixin.Observable" : "Ext.util.Observable", function() { var Observable = isMixin ? Ext.mixin.Observable : Ext.util.Observable, Boss, boss, bossConfig, bossListeners, bossAskListener, bossAskFn, bossFiredFn, bossFired2Fn, bossQuitFn, Employee, employee, employeeConfig, employeeListeners, employeeBubbleEvents, employeeAskListener, employeeAskFn, employeeFiredListener, employeeFiredFn, employeeQuitListener, employeeQuitFn, fakeScope; function makeDefaultListenerScope(o) { o.resolveListenerScope = function() { if (!this.defaultScope) { this.defaultScope = { meth1: function() { }, resolveListenerScope: function() { return null; } }; } return this.defaultScope; }; } function spyOnEvent(object, eventName, fn, options) { var listeners = Ext.apply({}, options), spy; listeners[eventName] = fn || Ext.emptyFn; spy = spyOn(listeners, eventName); object.addListener(listeners); return spy } beforeEach(function() { fakeScope = {}; // boss creation Boss = Ext.extend(Observable, { constructor: function(conf) { conf = conf || {}; this.listeners = conf.listeners; Boss.superclass.constructor.call(this, conf); }, doSomething: function() { } }); bossFiredFn = jasmine.createSpy("bossFiredFn"); bossFired2Fn = jasmine.createSpy("bossFired2Fn"); bossQuitFn = jasmine.createSpy("bossQuitFn"); bossAskFn = jasmine.createSpy("bossAskFn"); bossAskListener = { fn: bossAskFn, scope: fakeScope }; bossListeners = { ask_salary_augmentation: bossAskListener }; bossConfig = { listeners: bossListeners }; boss = new Boss(bossConfig); // employee creation Employee = Ext.extend(Observable, { constructor: function(conf) { conf = conf || {}; this.listeners = conf.listeners; Employee.superclass.constructor.call(this, conf); }, getBubbleTarget: function() { return this.boss; } }); employeeFiredFn = jasmine.createSpy("employeeFiredFn"); employeeQuitFn = jasmine.createSpy("employeeQuitFn"); employeeAskFn = jasmine.createSpy("employeeAskFn"); employeeFiredListener = { fn: employeeFiredFn, scope: fakeScope }; employeeQuitListener = { fn: employeeQuitFn, scope: fakeScope }; employeeAskListener = { fn: employeeAskFn, scope: fakeScope }; employeeListeners = { ask_salary_augmentation: employeeAskListener, fired: employeeFiredListener, quit: employeeQuitListener }; employeeBubbleEvents = ['ask_salary_augmentation']; employeeConfig = { listeners: employeeListeners, bubbleEvents: employeeBubbleEvents, boss: boss }; // some spies used in constructor spyOn(Employee.prototype, "on").andCallThrough(); spyOn(Employee.prototype, "addListener").andCallThrough(); spyOn(Employee.prototype, "enableBubble").andCallThrough(); employee = new Employee(employeeConfig); }); afterEach(function() { Observable.prototype.fireEventArgs.target = null; }); describe("constructor", function() { it("should allow the constructor to be called multiple times", function() { // In this test a class (Cls) uses two mixins that derive from Observable // and also mixes in observable itself. The class calls the constructor // of both mixins and the Observable constructor from its constructor. // This results in 3 calls to the Observable constructor. Since all 3 of // these calls are equivalent, only the first one should have any effect. // Successive calls should not re-initialize anything that was already // initialized by the first constructor call. var MixinA = Ext.define(null, { extend: Observable }), MixinB = Ext.define(null, { extend: Observable }), Cls = Ext.define(null, { mixins: { mixinA: MixinA, mixinB: MixinB, observable: Observable }, constructor: function(config) { var initConfig = spyOn(this, 'initConfig').andCallThrough(), isMixinObservable = Observable.$className === 'Ext.mixin.Observable', hasListeners, events; this.mixins.mixinA.constructor.call(this, config); if (isMixinObservable) { expect(initConfig).toHaveBeenCalledWith({foo: 'bar'}); } else { expect(this.foo).toBe('bar'); } // After the first invocation of the constructor a couple objects // are created. Cache these so we can make sure that successive // invocations do not recreate these objects hasListeners = this.hasListeners; events = this.events; this.mixins.observable.constructor.call(this, config); expect(this.hasListeners).toBe(hasListeners); expect(this.events).toBe(events); this.mixins.mixinB.constructor.call(this, config); expect(this.hasListeners).toBe(hasListeners); expect(this.events).toBe(events); if (isMixinObservable) { expect(initConfig.callCount).toBe(1); } } }); new Cls({foo: 'bar'}); }); }); describe("destroyable", function() { describe('listeners', function() { it('should remove the listeners when you destroy the returned Destroyable', function() { var newBoss = new Boss(), listenerDestroyable = newBoss.on({ fired: function() { }, quit: function() { }, ask_salary_augmentation: function() { }, destroyable: true }); expect(newBoss.hasListeners.fired).toEqual(1); expect(newBoss.hasListeners.quit).toEqual(1); expect(newBoss.hasListeners.ask_salary_augmentation).toEqual(1); listenerDestroyable.destroy(); expect(newBoss.hasListeners.fired).toBeUndefined(); expect(newBoss.hasListeners.quit).toBeUndefined(); expect(newBoss.hasListeners.ask_salary_augmentation).toBeUndefined(); }); }); describe('managed listeners', function() { it('should remove managed listeners when you destroy the returned Destroyable', function() { var newBoss = new Boss(), listenerDestroyable = newBoss.mon(newBoss, { fired: function() { }, quit: function() { }, ask_salary_augmentation: function() { }, destroyable: true }); expect(newBoss.hasListeners.fired).toEqual(1); expect(newBoss.hasListeners.quit).toEqual(1); expect(newBoss.hasListeners.ask_salary_augmentation).toEqual(1); listenerDestroyable.destroy(); expect(newBoss.hasListeners.fired).toBeUndefined(); expect(newBoss.hasListeners.quit).toBeUndefined(); expect(newBoss.hasListeners.ask_salary_augmentation).toBeUndefined(); }); }); describe('relayers', function() { it('should remove relayers when you destroy the returned Destroyable', function() { var newBoss = new Boss(), newEmployee = new Employee(), relayers = newBoss.relayEvents(newEmployee, ['fired', 'quit', 'ask_salary_augmentation'], 'minion_'), quit = 0, fired = 0, ask_salary_augmentation = 0; newBoss.on({ minion_fired: function() { fired++; }, minion_quit: function() { quit++; }, minion_ask_salary_augmentation: function() { ask_salary_augmentation++; } }); // Employee's events now have the relayers as listeners expect(newEmployee.hasListeners.fired).toEqual(1); expect(newEmployee.hasListeners.quit).toEqual(1); expect(newEmployee.hasListeners.ask_salary_augmentation).toEqual(1); // Fire the Employee events which should be relayed through the Boss newEmployee.fireEvent('fired'); newEmployee.fireEvent('quit'); newEmployee.fireEvent('ask_salary_augmentation'); expect(fired).toEqual(1); expect(quit).toEqual(1); expect(ask_salary_augmentation).toEqual(1); // Destroy the relayers, employee's events now should have no listeners relayers.destroy(); expect(newEmployee.hasListeners.fired).toBeUndefined(); expect(newEmployee.hasListeners.quit).toBeUndefined(); expect(newEmployee.hasListeners.ask_salary_augmentation).toBeUndefined(); // Fire the Employee events which should **NOT** be relayed through the Boss // The counters should remain at 1 newEmployee.fireEvent('fired'); newEmployee.fireEvent('quit'); newEmployee.fireEvent('ask_salary_augmentation'); expect(fired).toEqual(1); expect(quit).toEqual(1); expect(ask_salary_augmentation).toEqual(1); }); }); }); describe("instantiation", function() { describe("config initialization", function() { if (Observable === Ext.mixin.Observable) { it("should invoke initConfig", function() { var Foo = Ext.define(null, { extend: Observable }); spyOn(Foo.prototype, 'initConfig'); spyOn(Ext, 'apply'); var foo = new Foo({x: 1}); expect(Foo.prototype.initConfig).toHaveBeenCalledWith({x: 1}); expect(Ext.apply).not.toHaveBeenCalled(); foo.destroy(); }); it("should apply configuration if $applyConfigs is true", function() { var Foo = Ext.define(null, { extend: Observable, $applyConfigs: true }); spyOn(Foo.prototype, 'initConfig'); spyOn(Ext, 'apply'); var foo = new Foo({x: 1}); expect(Ext.apply).toHaveBeenCalledWith(foo, {x: 1}); expect(Foo.prototype.initConfig).not.toHaveBeenCalled(); foo.destroy(); }); } else { it("should apply configuration", function() { var Foo = Ext.define(null, { extend: Observable }); spyOn(Foo.prototype, 'initConfig'); spyOn(Ext, 'apply'); var foo = new Foo({x: 1}); expect(Ext.apply).toHaveBeenCalledWith(foo, {x: 1}); expect(Foo.prototype.initConfig).not.toHaveBeenCalled(); foo.destroy(); }); it("should invoke initConfig if $applyConfigs is false", function() { var Foo = Ext.define(null, { extend: Observable, $applyConfigs: false }); spyOn(Foo.prototype, 'initConfig'); spyOn(Ext, 'apply'); var foo = new Foo({x: 1}); expect(Foo.prototype.initConfig).toHaveBeenCalledWith({x: 1}); expect(Ext.apply).not.toHaveBeenCalled(); foo.destroy(); }); } }); it("should append event handlers passed in configuration params", function() { expect(Employee.prototype.addListener).toHaveBeenCalledWith(employeeListeners); }); it("should delete listeners configuration property", function() { expect(employee.listeners).toBeNull(); }); it("should enable bubble", function() { expect(Employee.prototype.enableBubble).toHaveBeenCalledWith(employeeBubbleEvents); }); }); describe("event name normalization", function() { var spy, o; beforeEach(function() { spy = jasmine.createSpy(); o = new Observable(); }); describe("firing", function() { it("should match when firing with lower case", function() { o.on('FOO', spy); o.fireEvent('foo'); expect(spy).toHaveBeenCalled(); }); it("should match when firing with mixed case", function() { o.on('foo', spy); o.fireEvent('FOO'); expect(spy).toHaveBeenCalled(); }); describe("using mon", function() { var o2; beforeEach(function() { o2 = new Observable(); }); it("should match when firing with lower case", function() { o2.mon(o, 'FOO', spy); o.fireEvent('foo'); expect(spy).toHaveBeenCalled(); }); it("should match when firing with mixed case", function() { o2.mon(o, 'foo', spy); o.fireEvent('FOO'); expect(spy).toHaveBeenCalled(); }); }); }); describe("removing", function() { it("should match when removing with lower case", function() { o.on('FOO', spy); o.un('foo', spy); o.fireEvent('foo'); expect(spy).not.toHaveBeenCalled(); }); it("should match when removing with mixed case", function() { o.on('foo', spy); o.un('FOO', spy); o.fireEvent('FOO'); expect(spy).not.toHaveBeenCalled(); }); describe("using mon/mun", function() { var o2; beforeEach(function() { o2 = new Observable(); }); it("should match when removing with lower case", function() { o2.mon(o, 'FOO', spy); o2.mun(o, 'foo', spy); o.fireEvent('foo'); expect(spy).not.toHaveBeenCalled(); }); it("should match when removing with mixed case", function() { o2.mon(o, 'foo', spy); o2.mun(o, 'FOO', spy); o.fireEvent('FOO'); expect(spy).not.toHaveBeenCalled(); }); }); }); describe("hasListener(s)", function() { it("should use lower case for hasListeners", function() { o.on('FOO', spy); expect(o.hasListeners.foo).toBe(1); }); it("should use lower case for hasListener", function() { o.on('FOO', spy); expect(o.hasListener('foo')).toBe(true); }); describe("using mon", function() { var o2; beforeEach(function() { o2 = new Observable(); }); it("should use lower case for hasListeners", function() { o2.mon(o, 'FOO', spy); expect(o.hasListeners.foo).toBe(1); }); it("should use lower case for hasListener", function() { o2.mon(o, 'FOO', spy); expect(o.hasListener('foo')).toBe(true); }); }); }); describe("suspend/resume", function() { it("should ignore case when asking if an event is suspended", function() { o.suspendEvent('FOO'); expect(o.isSuspended('foo')).toBe(true); }); it("should ignore case when resuming events", function() { o.on('foo', spy); o.suspendEvent('FOO'); o.fireEvent('foo'); expect(spy).not.toHaveBeenCalled(); o.resumeEvent('FoO'); o.fireEvent('fOo'); expect(spy).toHaveBeenCalled(); }); }); describe("bubbling", function() { it("should ignore case when bubbling events", function() { var other = new Observable(); other.on('foo', spy); o.enableBubble('FOO'); o.getBubbleTarget = function() { return other; }; o.fireEvent('foo'); expect(spy).toHaveBeenCalled(); }); }); }); describe("firing events", function() { describe("without options", function() { beforeEach(function() { employee.fireEvent("fired", "I'am fired :s"); }); describe("bubbling", function() { it("should not fire boss fired event", function() { expect(bossFiredFn).not.toHaveBeenCalled(); }); }); it("should call the handler only one times", function() { expect(employeeFiredFn.callCount).toEqual(1); }); it("should call the handler function with passed arguments", function() { expect(employeeFiredFn).toHaveBeenCalledWith("I'am fired :s", employeeFiredListener); }); it("should call the handler function with the correct scope", function() { expect(employeeFiredFn.calls[0].object).toBe(fakeScope); }); describe("scope resolution", function() { describe("with a function reference", function() { it("should resolve to the instance with scope:'this'", function() { var spy = jasmine.createSpy(); boss.on('fired', spy, 'this'); boss.fireEvent('fired'); expect(spy.mostRecentCall.object).toBe(boss); }); it("should throw an error with scope:'controller'", function() { var spy = jasmine.createSpy(); boss.on('fired', spy, 'controller'); expect(function() { boss.fireEvent('fired'); }).toThrow(); }); }); describe("with scope: 'this'", function() { it("resolve to the observable", function() { boss.on('fired', 'doSomething', 'this'); var spy = spyOn(boss, 'doSomething'); boss.fireEvent('fired'); expect(spy).toHaveBeenCalled(); }); }); describe("with scope: 'controller'", function() { it("not be able to resolve", function() { boss.on('fired', 'doSomething', 'controller'); expect(function() { boss.fireEvent('fired'); }).toThrow( 'scope: "controller" can only be specified on classes that derive from Ext.Component or Ext.Widget' ); }); }); describe("without default listener scope", function() { it("should always fire on the passed scope", function() { var o = { aMethod: function() { } }; var spy = spyOn(o, 'aMethod'); boss.on('fired', 'aMethod', o); boss.fireEvent('fired'); expect(spy).toHaveBeenCalled(); }); it("should default to the observable", function() { boss.aMethod = function() { }; var spy = spyOn(boss, 'aMethod'); boss.on('fired', 'aMethod'); boss.fireEvent('fired'); expect(spy).toHaveBeenCalled(); }); }); describe("with default listener scope", function() { beforeEach(function() { makeDefaultListenerScope(boss); }); it("should favour a passed scope", function() { var o = { aMethod: function() { } }; var spy = spyOn(o, 'aMethod'); boss.on('fired', 'aMethod', o); boss.fireEvent('fired'); expect(spy).toHaveBeenCalled(); }); it("should favour a default listener scope over the observable", function() { var spy = spyOn(boss.resolveListenerScope(), 'meth1'); boss.on('fired', 'meth1'); boss.fireEvent('fired'); expect(spy).toHaveBeenCalled(); }); it("should not cache the listener scope", function() { var other = { meth1: function() { } }, spy1 = spyOn(boss.resolveListenerScope(), 'meth1'), spy2 = spyOn(other, 'meth1'); boss.on('fired', 'meth1'); boss.fireEvent('fired'); expect(spy1).toHaveBeenCalled(); expect(spy2).not.toHaveBeenCalled(); spy1.reset(); spy2.reset(); boss.resolveListenerScope = function() { return other; }; boss.fireEvent('fired'); expect(spy1).not.toHaveBeenCalled(); expect(spy2).toHaveBeenCalled(); }); }); }); }); describe("with options", function() { describe("single", function() { var singleFn; beforeEach(function() { singleFn = jasmine.createSpy("singleFn"); boss.addListener("singleevent", singleFn, fakeScope, { single: true }); boss.fireEvent("singleevent", "single 1"); boss.fireEvent("singleevent", "single 2"); boss.fireEvent("singleevent", "single 3"); }); it("should call the handler only one times", function() { expect(singleFn.callCount).toEqual(1); }); it("should call the handler function with passed arguments", function() { expect(singleFn).toHaveBeenCalledWith("single 1", { single: true }); }); it("should call the handler function with the correct scope", function() { expect(singleFn.calls[0].object).toBe(fakeScope); }); it("should remove the listener", function() { expect(boss.hasListener("singleevent")).toBe(false); }); it("should fire with dynamic scope resoution", function() { boss = new Boss(); makeDefaultListenerScope(boss); var spy = spyOn(boss.resolveListenerScope(), 'meth1'); boss.addListener("singleevent", 'meth1', undefined, { single: true }); boss.fireEvent("singleevent", "single 1"); boss.fireEvent("singleevent", "single 2"); boss.fireEvent("singleevent", "single 3"); expect(spy.callCount).toBe(1); }); }); describe("target", function() { var ct, callbackFn, callbackFn2; beforeEach(function() { ct = Ext.create('Ext.container.Container', { items: [{ bubbleEvents: ['add', 'remove'], xtype: 'container', itemId: 'foo', items: [{ bubbleEvents: ['add', 'remove'], xtype: 'component', itemId: 'bar' }, { bubbleEvents: ['add', 'remove'], xtype: 'component', itemId: 'baz' }] }] }); callbackFn = jasmine.createSpy('callbackFn'); callbackFn2 = jasmine.createSpy('callbackFn2'); }); afterEach(function() { ct.destroy(); }); it("should bubble up to its owner containers when target is not defined", function() { ct.on( 'remove', callbackFn ); ct.getComponent('foo').on( 'remove', callbackFn2 ); ct.getComponent('foo').remove('bar'); expect(callbackFn).toHaveBeenCalled(); expect(callbackFn2).toHaveBeenCalled(); }); it("should not bubble up to its owner containers when target is defined on a different observable", function() { ct.on( 'remove', callbackFn, ct, {target: ct} ); ct.getComponent('foo').on( 'remove', callbackFn2, ct, {target: ct} ); ct.getComponent('foo').remove('baz'); expect(callbackFn).not.toHaveBeenCalled(); expect(callbackFn2).not.toHaveBeenCalled(); }); it("should not bubble up to its owner container but will bubble up to its ancestor", function() { ct.on( 'add', callbackFn ); ct.getComponent('foo').on( 'add', callbackFn2, ct, {target: ct} ); ct.getComponent('foo').add({ xtype: 'component', itemId: 'test' }); expect(callbackFn).toHaveBeenCalled(); expect(callbackFn2).not.toHaveBeenCalled(); }); it("should fire with dynamic scope resolution", function() { makeDefaultListenerScope(ct); var spy = spyOn(ct.resolveListenerScope(), 'meth1'); ct.on('add', 'meth1', undefined, { target: ct }); ct.add({ xtype: 'component', itemId: 'test' }); expect(spy).toHaveBeenCalled(); }); }); describe("buffer", function() { var bufferFn; beforeEach(function() { bufferFn = jasmine.createSpy("bufferFn"); boss.addListener("bufferevent", bufferFn, fakeScope, { buffer: 5 }); boss.fireEvent("bufferevent", "buffer 1"); boss.fireEvent("bufferevent", "buffer 2"); boss.fireEvent("bufferevent", "buffer 3"); }); it("should not call handler immediately", function() { expect(bufferFn).not.toHaveBeenCalled(); }); it("should call the handler only one times after a certain amount of time", function() { waitsFor(function() { return bufferFn.callCount === 1; }, "bufferFn wasn't called"); }); it("should call the handler function with passed arguments coming from the last event firing", function() { waitsFor(function() { return bufferFn.callCount === 1; }, "bufferFn wasn't called"); runs(function() { expect(bufferFn).toHaveBeenCalledWith("buffer 3", { buffer: 5 }); }); }); it("should call the handler function with the correct scope", function() { waitsFor(function() { return bufferFn.callCount === 1; }, "bufferFn wasn't called"); runs(function() { expect(bufferFn.calls[0].object).toBe(fakeScope); }); }); it("should not remove the listener", function() { waitsFor(function() { return bufferFn.callCount === 1; }, "bufferFn wasn't called"); runs(function() { expect(boss.hasListener("bufferevent")).toBe(true); }); }); it("should fire with dynamic scope resolution", function() { boss = new Boss(); makeDefaultListenerScope(boss); var spy = spyOn(boss.resolveListenerScope(), 'meth1'); boss.on("bufferevent", 'meth1', undefined, { buffer: 5 }); boss.fireEvent("bufferevent", "buffer 1"); boss.fireEvent("bufferevent", "buffer 2"); boss.fireEvent("bufferevent", "buffer 3"); waitsFor(function() { return spy.callCount === 1; }, "spy wasn't called"); runs(function() { expect(spy.callCount).toBe(1); }); }); }); describe("delay", function() { var delayFn; beforeEach(function() { delayFn = jasmine.createSpy("delayFn"); boss.addListener("delayevent", delayFn, fakeScope, { delay: 5 }); boss.fireEvent("delayevent", "delay"); }); it("should not call handler immediately", function() { expect(delayFn).not.toHaveBeenCalled(); }); it("should call the handler only one times after a certain amount of time", function() { waitsFor(function() { return delayFn.callCount === 1; }, "delayFn wasn't called"); }); it("should call the handler function with passed arguments", function() { waitsFor(function() { return delayFn.callCount === 1; }, "delayFn wasn't called"); runs(function() { expect(delayFn).toHaveBeenCalledWith("delay", { delay: 5 }); }); }); it("should call the handler function with the correct scope", function() { waitsFor(function() { return delayFn.callCount === 1; }, "delayFn wasn't called"); runs(function() { expect(delayFn.calls[0].object).toBe(fakeScope); }); }); it("should fire with dynamic scope resolution", function() { boss = new Boss(); makeDefaultListenerScope(boss); var spy = spyOn(boss.resolveListenerScope(), 'meth1'); boss.on("delayevent", 'meth1', undefined, { delay: 5 }); boss.fireEvent("delayevent", "buffer 1"); waitsFor(function() { return spy.callCount === 1; }, "spy wasn't called"); runs(function() { expect(spy).toHaveBeenCalled(); }); }); }); describe("priority", function() { var a, result; beforeEach(function() { Ext.define('A', { extend: Observable }); a = new A(); result = []; }); afterEach(function() { Ext.undefine('A'); }); it("should call the handlers in priority order", function() { a.on('foo', function() { result.push(10); }, null, {priority: 10}); a.on('foo', function() { result.push('u1'); }, null); a.on('foo', function() { result.push(-7); }, null, {priority: -7}); a.on('foo', function() { result.push(0); }, null, {priority: 0}); a.on('foo', function() { result.push(5); }, null, {priority: 5}); a.on('foo', function() { result.push(-3); }, null, {priority: -3}); a.on('foo', function() { result.push('u2'); }); a.fireEvent('foo'); expect(result.join(' ')).toBe('10 5 u1 0 u2 -3 -7'); }); it("should add a 0 priority listener after removal of a positive priority listener, when the listeners array contains negative priority listeners", function() { // This spec is needed because of the inner workings of the priority // mechanism. Internally, to avoid excessive looping, it tracks a // highestNegativePriorityIndex so that when a 0 or undefined priority // listener is added it can simply be inserted before the listener // with the highest negative index. This spec ensures the internal // index gets updated when listeners are removed. function f10() { result.push(10); } a.on('foo', function() { result.push('u1'); }); a.on('foo', f10, null, {priority: 10}); a.on('foo', function() { result.push(-7); }, null, {priority: -7}); a.on('foo', function() { result.push(5); }, null, {priority: 5}); a.un('foo', f10); a.on('foo', function() { result.push('u2'); }); a.fireEvent('foo'); expect(result.join(' ')).toBe('5 u1 u2 -7'); }); }); describe("order", function() { var a, result; beforeEach(function() { Ext.define('A', { extend: Observable }); a = new A(); result = []; }); afterEach(function() { Ext.undefine('A'); }); it("should fire events in the correct order using the order event option", function() { a.on('foo', function() { result.push(101); }, null, {priority: 101}); a.on('foo', function() { result.push('after'); }, null, {order: 'after'}); a.on('foo', function() { result.push(-101); }, null, {priority: -101}); a.on('foo', function() { result.push('before'); }, null, {order: 'before'}); a.on('foo', function() { result.push('current'); }, null, {order: 'current'}); a.on('foo', function() { result.push(0); }, null, {priority: 0}); a.fireEvent('foo'); expect(result.join(' ')).toBe('101 before current 0 after -101'); }); it("should fire events in the correct order using the order method parameter", function() { a.on('foo', function() { result.push(101); }, null, {priority: 101}); a.on('foo', function() { result.push('after'); }, null, null, 'after'); a.on('foo', function() { result.push(-101); }, null, {priority: -101}); a.on('foo', function() { result.push('before'); }, null, null, 'before'); a.on('foo', function() { result.push('current'); }, null, null, 'current'); a.on('foo', function() { result.push(0); }, null, {priority: 0}); a.fireEvent('foo'); expect(result.join(' ')).toBe('101 before current 0 after -101'); }); }); }); describe("return value", function() { var fn1, fn2, fn3, fn4, fn1Called, fn2Called, fn3Called, fn4Called = false; beforeEach(function() { fn1Called = fn2Called = fn3Called = fn4Called = false; fn1 = function() { fn1Called = true; return true; }; fn2 = function() { fn2Called = true; return true; }; fn3 = function() { fn3Called = true; return true; }; fn4 = function() { fn4Called = true; return false; }; }); afterEach(function() { fn1 = fn2 = fn3 = fn4 = null; }); it("should return true if there are no listeners", function() { employee = new Employee(); expect(employee.fireEvent('quit')).toBe(true); }); it("should return true if none of the listeners return false", function() { employee = new Employee(); employee.on('quit', fn1); employee.on('quit', fn2); employee.on('quit', fn3); expect(employee.fireEvent('quit')).toBe(true); }); it("should return false if any handler returns false", function() { employee = new Employee(); employee.on('quit', fn1); employee.on('quit', fn4); employee.on('quit', fn3); expect(employee.fireEvent('quit')).toBe(false); }); it("should stop firing once a listener returns false", function() { employee = new Employee(); employee.on('quit', fn1); employee.on('quit', fn4); employee.on('quit', fn2); employee.on('quit', fn3); employee.fireEvent('quit'); expect(fn1Called).toBe(true); expect(fn4Called).toBe(true); expect(fn2Called).toBe(false); expect(fn3Called).toBe(false); }); }); // https://sencha.jira.com/browse/EXTJS-22353 // Three different but related bugs here, so three suites describe("during scope destruction", function() { var employee2, listenerSpy; beforeEach(function() { spyOn(Ext, 'raise'); listenerSpy = jasmine.createSpy('listener'); employee2 = new Employee(); employee.on('foo', function() { employee2.destroy(); }); }); afterEach(function() { listenerSpy = employee2 = Ext.destroy(employee2); }); describe("with non-managed listener", function() { beforeEach(function() { employee.on('foo', listenerSpy, employee2); }); it("should not throw an exception", function() { expect(function() { employee.fireEvent('foo'); }).not.toThrow(); }); it("should not fire the listener", function() { employee.fireEvent('foo'); expect(listenerSpy).not.toHaveBeenCalled(); }); it("should remove the listener", function() { employee.fireEvent('foo'); // The destruction listener above expect(employee.hasListeners.foo).toBe(1); }); }); describe("with managed listener", function() { beforeEach(function() { employee2.mon(employee, 'foo', listenerSpy, employee2); }); it("should not throw an exception", function() { expect(function() { employee.fireEvent('foo'); }).not.toThrow(); }); it("should not fire the listener", function() { employee.fireEvent('foo');