mirror of
https://github.com/adobe/brackets.git
synced 2024-11-20 18:02:54 +01:00
454 lines
16 KiB
JavaScript
454 lines
16 KiB
JavaScript
/*
|
|
* Copyright (c) 2013 - present Adobe Systems Incorporated. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
/*global jasmine, describe, beforeEach, it, expect, spyOn */
|
|
|
|
define(function (require, exports, module) {
|
|
"use strict";
|
|
|
|
var EventDispatcher = require("utils/EventDispatcher");
|
|
|
|
describe("EventDispatcher", function () {
|
|
var dispatcher,
|
|
fn1,
|
|
fn2,
|
|
fn3,
|
|
fn4;
|
|
|
|
beforeEach(function () {
|
|
dispatcher = {};
|
|
EventDispatcher.makeEventDispatcher(dispatcher);
|
|
|
|
fn1 = jasmine.createSpy();
|
|
fn2 = jasmine.createSpy();
|
|
fn3 = jasmine.createSpy();
|
|
fn4 = jasmine.createSpy();
|
|
});
|
|
|
|
it("should dispatch when no handlers for any event", function () {
|
|
dispatcher.trigger("foo"); // shouldn't throw
|
|
});
|
|
|
|
it("should dispatch when no handlers for this event", function () {
|
|
dispatcher.on("bar", fn1);
|
|
dispatcher.trigger("foo"); // shouldn't throw
|
|
});
|
|
|
|
it("should attach handlers that receive events", function () {
|
|
dispatcher.on("foo", fn1).on("foo", fn2);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
|
|
expect(fn1.mostRecentCall.args[0]).toEqual({ type: "foo", target: dispatcher });
|
|
});
|
|
|
|
it("should receive events with arguments", function () {
|
|
dispatcher.on("foo", fn1).on("foo", fn2);
|
|
dispatcher.trigger("foo", 42, "bar");
|
|
expect(fn1).toHaveBeenCalledWith(jasmine.any(Object), 42, "bar");
|
|
expect(fn2).toHaveBeenCalledWith(jasmine.any(Object), 42, "bar");
|
|
});
|
|
|
|
it("should separate handlers by event", function () {
|
|
dispatcher.on("foo", fn1).on("bar", fn2);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).not.toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
fn2.reset();
|
|
dispatcher.trigger("bar");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call handlers in the order added", function () {
|
|
var order = 1;
|
|
dispatcher.on("foo", function () { expect(order).toBe(1); order++; });
|
|
dispatcher.on("foo", function () { expect(order).toBe(2); order++; });
|
|
dispatcher.on("foo", function () { expect(order).toBe(3); order++; });
|
|
dispatcher.on("foo", function () { expect(order).toBe(4); order++; });
|
|
dispatcher.trigger("foo");
|
|
});
|
|
|
|
|
|
it("should detach handlers by function", function () {
|
|
dispatcher.on("foo", fn1).on("foo", fn2).on("foo", fn3).on("foo", fn4);
|
|
|
|
dispatcher.off("foo", fn2).off("foo", fn4);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).not.toHaveBeenCalled();
|
|
expect(fn3).toHaveBeenCalled();
|
|
expect(fn4).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should detach handlers by event alone", function () {
|
|
dispatcher.on("foo", fn1).on("bar", fn2);
|
|
dispatcher.off("foo");
|
|
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
expect(fn2).not.toHaveBeenCalled();
|
|
|
|
dispatcher.trigger("bar");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should detach handlers by namespace alone", function () {
|
|
dispatcher.on("foo.1", fn1).on("foo.2", fn2).on("foo", fn3).on("bar.1", fn4);
|
|
dispatcher.off(".1");
|
|
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
expect(fn3).toHaveBeenCalled();
|
|
|
|
dispatcher.trigger("bar");
|
|
expect(fn4).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should detach handlers by event.namespace", function () {
|
|
dispatcher.on("foo.1", fn1).on("foo.2", fn2).on("bar", fn3).on("bar.2", fn4);
|
|
dispatcher.off("bar.2");
|
|
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
|
|
dispatcher.trigger("bar");
|
|
expect(fn3).toHaveBeenCalled();
|
|
expect(fn4).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should detach by .namespace when no listeners present (and still chain)", function () {
|
|
dispatcher.off(".1").on("foo.1", fn1);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should no-op detach by event alone when no handlers for any event", function () {
|
|
dispatcher.off("foo"); // shouldn't throw
|
|
});
|
|
|
|
it("should no-op detach by event alone when no handlers for this event", function () {
|
|
dispatcher.on("foo", fn1);
|
|
dispatcher.off("bar"); // shouldn't throw
|
|
});
|
|
|
|
it("should no-op detach by function when that function not attached", function () {
|
|
dispatcher.on("foo", fn1);
|
|
dispatcher.off("foo", fn2); // shouldn't throw
|
|
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should no-op detach by namespace alone when that namespace not used", function () {
|
|
dispatcher.on("foo.1", fn1);
|
|
dispatcher.off(".2"); // shouldn't throw
|
|
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should no-op detach by event.namespace when that pairing not used", function () {
|
|
dispatcher.on("foo.1", fn1);
|
|
dispatcher.off("bar.1"); // shouldn't throw
|
|
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
dispatcher.off("foo.4"); // shouldn't throw
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should dispatch when no handlers after removing handlers", function () {
|
|
dispatcher.on("foo", fn1).on("foo", fn2);
|
|
dispatcher.off("foo", fn1).off("foo", fn2);
|
|
|
|
dispatcher.trigger("foo"); // shouldn't throw
|
|
});
|
|
|
|
|
|
it("should attach to multiple space-separated events", function () {
|
|
dispatcher.on("foo bar", fn1);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
dispatcher.trigger("bar");
|
|
expect(fn1).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should attach to multiple space-separated events with namespaces", function () {
|
|
dispatcher.on("foo.1 bar.2", fn1);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
dispatcher.trigger("bar");
|
|
expect(fn1).toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
dispatcher.off(".1");
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
dispatcher.trigger("bar");
|
|
expect(fn1).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should detach from multiple space-separated events", function () {
|
|
dispatcher.on("foo", fn1).on("bar", fn1);
|
|
dispatcher.off("foo bar", fn1);
|
|
dispatcher.trigger("foo");
|
|
dispatcher.trigger("bar");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should detach from multiple space-separated namespaces", function () {
|
|
dispatcher.on("foo.1", fn1).on("bar.2", fn1);
|
|
dispatcher.off(".1 .2", fn1);
|
|
dispatcher.trigger("foo");
|
|
dispatcher.trigger("bar");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should detach from multiple space-separated events with namespaces", function () {
|
|
dispatcher.on("foo.1", fn1).on("bar.2", fn1);
|
|
dispatcher.off("foo.1 bar.2", fn1);
|
|
dispatcher.trigger("foo");
|
|
dispatcher.trigger("bar");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
});
|
|
|
|
|
|
it("handlers should be independent per-instance even when attached to prototype", function () {
|
|
function SomeClass() {}
|
|
EventDispatcher.makeEventDispatcher(SomeClass.prototype);
|
|
|
|
var sc1 = new SomeClass();
|
|
var sc2 = new SomeClass();
|
|
sc1.on("foo", fn1);
|
|
sc2.on("foo", fn2);
|
|
|
|
sc1.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).not.toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
fn2.reset();
|
|
sc1.off("foo");
|
|
sc2.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
});
|
|
|
|
|
|
it("attaching handler multiple times should call it multiple times", function () {
|
|
dispatcher.on("foo", fn1).on("foo", fn1);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1.callCount).toBe(2);
|
|
});
|
|
|
|
it("duplicate handlers should all be detached at once", function () {
|
|
dispatcher.on("foo", fn1).on("foo", fn1);
|
|
dispatcher.off("foo", fn1);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("namespaces let duplicate handlers be detached separately", function () {
|
|
dispatcher.on("foo.1", fn1).on("foo.2", fn1);
|
|
dispatcher.off("foo.1", fn1);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1.callCount).toBe(1);
|
|
});
|
|
|
|
it("concurrent removals don't break trigger()", function () {
|
|
dispatcher.on("foo", function () {
|
|
dispatcher.off("foo", fn1).off("foo", fn2);
|
|
});
|
|
dispatcher.on("foo", fn1).on("foo", fn2);
|
|
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
fn2.reset();
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
expect(fn2).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("concurrent additions don't break trigger()", function () {
|
|
dispatcher.on("foo", function () {
|
|
dispatcher.on("foo", fn3);
|
|
});
|
|
dispatcher.on("foo", fn1).on("foo", fn2);
|
|
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
expect(fn3).not.toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
fn2.reset();
|
|
fn3.reset();
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
expect(fn3).toHaveBeenCalled();
|
|
});
|
|
|
|
|
|
it("handlers attached with one() are only called once", function () {
|
|
dispatcher.on("foo", fn1).one("foo", fn2).on("foo", fn3);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
expect(fn3).toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
fn2.reset();
|
|
fn3.reset();
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).not.toHaveBeenCalled();
|
|
expect(fn3).toHaveBeenCalled();
|
|
});
|
|
|
|
it("one() is independent per event", function () {
|
|
dispatcher.one("foo bar", fn1);
|
|
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
fn1.reset();
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
dispatcher.trigger("bar");
|
|
expect(fn1).toHaveBeenCalled();
|
|
fn1.reset();
|
|
dispatcher.trigger("bar");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("one() is independent per dispatcher", function () {
|
|
var dispatcher1 = {}, dispatcher2 = {};
|
|
EventDispatcher.makeEventDispatcher(dispatcher1);
|
|
EventDispatcher.makeEventDispatcher(dispatcher2);
|
|
|
|
dispatcher1.one("foo", fn1);
|
|
dispatcher2.one("foo", fn1);
|
|
|
|
dispatcher1.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
fn1.reset();
|
|
dispatcher1.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
dispatcher2.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
fn1.reset();
|
|
dispatcher2.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("duplicate one() listeners both run and both detach first time", function () {
|
|
dispatcher.one("foo", fn1).one("foo", fn1);
|
|
|
|
dispatcher.trigger("foo");
|
|
expect(fn1.callCount).toBe(2);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1.callCount).toBe(2);
|
|
});
|
|
|
|
it("off() given a function should work with one()", function () {
|
|
dispatcher.one("foo", fn1).one("foo", fn2);
|
|
dispatcher.off("foo", fn1);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled(); // ensure other fn's one() wrappers remain unaffected
|
|
});
|
|
|
|
it("off() given a namespace should work with one()", function () {
|
|
dispatcher.one("foo.1", fn1);
|
|
dispatcher.off(".1");
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).not.toHaveBeenCalled();
|
|
});
|
|
|
|
|
|
it("triggerWithArray() util accepts an array of event arguments", function () {
|
|
dispatcher.on("foo", fn1);
|
|
EventDispatcher.triggerWithArray(dispatcher, "foo", [42, "bar"]);
|
|
expect(fn1).toHaveBeenCalledWith(jasmine.any(Object), 42, "bar");
|
|
});
|
|
|
|
it("on_duringInit() attaches listeners before makeEventDispatcher()", function () {
|
|
dispatcher = {};
|
|
EventDispatcher.on_duringInit(dispatcher, "foo", fn1);
|
|
|
|
EventDispatcher.makeEventDispatcher(dispatcher);
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
|
|
fn1.reset();
|
|
dispatcher.on("foo", fn2); // add 2nd listener the normal way - shouldn't disrupt original listener
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
expect(fn2).toHaveBeenCalled();
|
|
});
|
|
|
|
|
|
it("on() should print warnings when too many listeners attached", function () {
|
|
function makeStubListener() { // avoids JSLint "don't make functions in a loop" complaint
|
|
return function () {};
|
|
}
|
|
|
|
spyOn(console, "error");
|
|
var i;
|
|
for (i = 0; i < 15; i++) {
|
|
dispatcher.on("foo", makeStubListener());
|
|
}
|
|
expect(console.error).not.toHaveBeenCalled();
|
|
|
|
// Prints warnings when number of listeners exceeds 15
|
|
dispatcher.on("foo", fn1);
|
|
expect(console.error).toHaveBeenCalled();
|
|
|
|
// ...but still attaches listener anyway
|
|
dispatcher.trigger("foo");
|
|
expect(fn1).toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|