mirror of
https://github.com/adobe/brackets.git
synced 2024-11-20 18:02:54 +01:00
666 lines
25 KiB
JavaScript
666 lines
25 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 describe, beforeEach, it, runs, waitsFor, waitsForDone, waitsForFail, expect */
|
|
|
|
define(function (require, exports, module) {
|
|
"use strict";
|
|
|
|
var Async = require("utils/Async"),
|
|
PromiseQueue = Async.PromiseQueue;
|
|
|
|
describe("Async", function () {
|
|
|
|
describe("Chain", function () {
|
|
|
|
function zeroArgThatSucceeds() {
|
|
var d = new $.Deferred();
|
|
setTimeout(function () {
|
|
d.resolve();
|
|
}, 1);
|
|
return d.promise();
|
|
}
|
|
|
|
function zeroArgThatFails() {
|
|
var d = new $.Deferred();
|
|
setTimeout(function () {
|
|
d.reject();
|
|
}, 1);
|
|
return d.promise();
|
|
}
|
|
|
|
function oneArgThatSucceeds(x) {
|
|
var d = new $.Deferred();
|
|
setTimeout(function () {
|
|
d.resolveWith(null, [x]);
|
|
}, 1);
|
|
return d.promise();
|
|
}
|
|
|
|
function twoArgThatSucceeds(x, y) {
|
|
var d = new $.Deferred();
|
|
setTimeout(function () {
|
|
d.resolveWith(null, [x, y]);
|
|
}, 1);
|
|
return d.promise();
|
|
}
|
|
|
|
function twoArgThatFails(x, y) {
|
|
var d = new $.Deferred();
|
|
setTimeout(function () {
|
|
d.rejectWith(null, [x, y]);
|
|
}, 1);
|
|
return d.promise();
|
|
}
|
|
|
|
function syncAddTwoArg(x, y) {
|
|
return x + y;
|
|
}
|
|
|
|
function syncException() {
|
|
throw new Error("sync error");
|
|
}
|
|
|
|
function createArrayComparator(arrayOne) {
|
|
return function (arrayTwo) {
|
|
var i, l;
|
|
expect(arrayOne.length).toBe(arrayTwo.length);
|
|
l = Math.min(arrayOne.length, arrayTwo.length);
|
|
for (i = 0; i < l; i++) {
|
|
expect(arrayOne[i]).toBe(arrayTwo[i]);
|
|
}
|
|
};
|
|
}
|
|
|
|
function expectChainHelper(functions, args, shouldSucceed, responseComparator) {
|
|
var done = false;
|
|
var success = false;
|
|
var response = null;
|
|
runs(function () {
|
|
var result = Async.chain(
|
|
functions,
|
|
args
|
|
);
|
|
result.done(function () {
|
|
done = true;
|
|
success = true;
|
|
response = arguments;
|
|
});
|
|
result.fail(function () {
|
|
done = true;
|
|
success = false;
|
|
response = arguments;
|
|
});
|
|
});
|
|
waitsFor(function () {
|
|
return done;
|
|
}, "The chain should complete", 100);
|
|
runs(function () {
|
|
expect(success).toBe(shouldSucceed);
|
|
responseComparator(response);
|
|
});
|
|
}
|
|
|
|
describe("Zero-argument deferreds", function () {
|
|
it("[zero-arg] work with a null argument array", function () {
|
|
expectChainHelper(
|
|
[zeroArgThatSucceeds, zeroArgThatSucceeds, zeroArgThatSucceeds],
|
|
null,
|
|
true,
|
|
createArrayComparator([])
|
|
);
|
|
});
|
|
|
|
it("[zero-arg] call error callback when first deferred fails", function () {
|
|
expectChainHelper(
|
|
[zeroArgThatFails, zeroArgThatSucceeds, zeroArgThatSucceeds],
|
|
[],
|
|
false,
|
|
createArrayComparator([])
|
|
);
|
|
});
|
|
|
|
it("[zero-arg] call error callback when middle deferred fails", function () {
|
|
expectChainHelper(
|
|
[zeroArgThatSucceeds, zeroArgThatFails, zeroArgThatSucceeds],
|
|
[],
|
|
false,
|
|
createArrayComparator([])
|
|
);
|
|
});
|
|
|
|
it("[zero-arg] call error callback when last deferred fails", function () {
|
|
expectChainHelper(
|
|
[zeroArgThatSucceeds, zeroArgThatSucceeds, zeroArgThatFails],
|
|
[],
|
|
false,
|
|
createArrayComparator([])
|
|
);
|
|
});
|
|
|
|
it("[zero-arg] call success callback when all deferreds succeed", function () {
|
|
expectChainHelper(
|
|
[zeroArgThatSucceeds, zeroArgThatSucceeds, zeroArgThatSucceeds],
|
|
[],
|
|
true,
|
|
createArrayComparator([])
|
|
);
|
|
});
|
|
|
|
it("[zero-arg] call success callback immediately if there are no deferreds", function () {
|
|
expectChainHelper(
|
|
[],
|
|
[],
|
|
true,
|
|
createArrayComparator([])
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Nonzero-argument deferreds", function () {
|
|
it("[nonzero-arg] call error callback when first deferred fails", function () {
|
|
expectChainHelper(
|
|
[twoArgThatFails, twoArgThatSucceeds, twoArgThatSucceeds],
|
|
[1, 2],
|
|
false,
|
|
createArrayComparator([1, 2])
|
|
);
|
|
});
|
|
|
|
it("[nonzero-arg] call error callback when middle deferred fails", function () {
|
|
expectChainHelper(
|
|
[twoArgThatSucceeds, twoArgThatFails, twoArgThatSucceeds],
|
|
[1, 2],
|
|
false,
|
|
createArrayComparator([1, 2])
|
|
);
|
|
});
|
|
|
|
it("[nonzero-arg] call error callback when last deferred fails", function () {
|
|
expectChainHelper(
|
|
[twoArgThatSucceeds, twoArgThatSucceeds, twoArgThatFails],
|
|
[1, 2],
|
|
false,
|
|
createArrayComparator([1, 2])
|
|
);
|
|
});
|
|
|
|
it("[nonzero-arg] call success callback when all deferreds succeed", function () {
|
|
expectChainHelper(
|
|
[twoArgThatSucceeds, twoArgThatSucceeds, twoArgThatSucceeds],
|
|
[1, 2],
|
|
true,
|
|
createArrayComparator([1, 2])
|
|
);
|
|
});
|
|
|
|
it("[nonzero-arg] call success callback immediately if there are no deferreds", function () {
|
|
expectChainHelper(
|
|
[],
|
|
[1, 2],
|
|
true,
|
|
createArrayComparator([1, 2])
|
|
);
|
|
});
|
|
});
|
|
|
|
|
|
describe("With Timeout", function () {
|
|
function promiseThatSucceeds(duration) {
|
|
var d = new $.Deferred();
|
|
setTimeout(function () {
|
|
d.resolve();
|
|
}, duration);
|
|
return d.promise();
|
|
}
|
|
|
|
function promiseThatFails(duration) {
|
|
var d = new $.Deferred();
|
|
setTimeout(function () {
|
|
d.reject();
|
|
}, duration);
|
|
return d.promise();
|
|
}
|
|
|
|
it("should resolve promise before rejected with timeout", function () {
|
|
var promiseBase, promiseWrapper;
|
|
|
|
runs(function () {
|
|
promiseBase = promiseThatSucceeds(5);
|
|
promiseWrapper = Async.withTimeout(promiseBase, 10);
|
|
waitsForDone(promiseWrapper, "promise resolves before timeout");
|
|
});
|
|
|
|
runs(function () {
|
|
// base promise resolves wrapper promise
|
|
expect(promiseBase.state()).toBe("resolved");
|
|
expect(promiseWrapper.state()).toBe("resolved");
|
|
});
|
|
});
|
|
|
|
it("should reject promise before resolved with timeout", function () {
|
|
var promiseBase, promiseWrapper;
|
|
|
|
runs(function () {
|
|
promiseBase = promiseThatFails(5);
|
|
promiseWrapper = Async.withTimeout(promiseBase, 10, true);
|
|
waitsForFail(promiseWrapper, "promise rejected before timeout");
|
|
});
|
|
|
|
runs(function () {
|
|
// base promise rejects wrapper promise
|
|
expect(promiseBase.state()).toBe("rejected");
|
|
expect(promiseWrapper.state()).toBe("rejected");
|
|
});
|
|
});
|
|
|
|
it("should timeout with reject before promise resolves", function () {
|
|
var promiseBase, promiseWrapper;
|
|
|
|
runs(function () {
|
|
promiseBase = promiseThatSucceeds(10);
|
|
promiseWrapper = Async.withTimeout(promiseBase, 5);
|
|
waitsForFail(promiseWrapper, "times out before promise resolves");
|
|
});
|
|
|
|
runs(function () {
|
|
expect(promiseWrapper.state()).toBe("rejected");
|
|
waitsForDone(promiseBase, "promise resolves after timeout");
|
|
});
|
|
|
|
runs(function () {
|
|
expect(promiseBase.state()).toBe("resolved");
|
|
});
|
|
});
|
|
|
|
it("should timeout with resolve before promise rejected", function () {
|
|
var promiseBase, promiseWrapper;
|
|
|
|
runs(function () {
|
|
promiseBase = promiseThatFails(10);
|
|
promiseWrapper = Async.withTimeout(promiseBase, 5, true);
|
|
waitsForDone(promiseWrapper, "times out before promise is rejected");
|
|
});
|
|
|
|
runs(function () {
|
|
expect(promiseWrapper.state()).toBe("resolved");
|
|
waitsForFail(promiseBase, "promise is rejected after timeout");
|
|
});
|
|
|
|
runs(function () {
|
|
expect(promiseBase.state()).toBe("rejected");
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
describe("Async/sync mix", function () {
|
|
it("[async/sync] succeed with sync command at beginning", function () {
|
|
expectChainHelper(
|
|
[syncAddTwoArg, oneArgThatSucceeds, oneArgThatSucceeds],
|
|
[1, 2],
|
|
true,
|
|
createArrayComparator([3])
|
|
);
|
|
});
|
|
|
|
it("[async/sync] succeed with sync command at middle", function () {
|
|
expectChainHelper(
|
|
[twoArgThatSucceeds, syncAddTwoArg, oneArgThatSucceeds],
|
|
[1, 2],
|
|
true,
|
|
createArrayComparator([3])
|
|
);
|
|
});
|
|
|
|
it("[async/sync] succeed with sync command at end", function () {
|
|
expectChainHelper(
|
|
[twoArgThatSucceeds, twoArgThatSucceeds, syncAddTwoArg],
|
|
[1, 2],
|
|
true,
|
|
createArrayComparator([3])
|
|
);
|
|
});
|
|
|
|
it("[async/sync] call error callback if sync command fails", function () {
|
|
expectChainHelper(
|
|
[twoArgThatSucceeds, syncException, twoArgThatSucceeds],
|
|
[1, 2],
|
|
false,
|
|
function (response) {
|
|
expect(response.length).toBe(1);
|
|
expect(response[0] instanceof Error).toBe(true);
|
|
}
|
|
);
|
|
});
|
|
|
|
it("[async/sync] two sync commands in order are executed completely synchronously", function () {
|
|
var promise = null,
|
|
flag = true;
|
|
|
|
runs(function () {
|
|
|
|
function negate(b) {
|
|
return !b;
|
|
}
|
|
function setFlag(b) {
|
|
flag = b;
|
|
return flag;
|
|
}
|
|
|
|
promise = Async.chain([negate, setFlag], [flag]);
|
|
promise.done(function (b) {
|
|
expect(b).toBe(flag);
|
|
});
|
|
|
|
// note, we WANT to test this synchronously. This is not a bug
|
|
// in the unit test. A series of synchronous functions should
|
|
// execute synchronously.
|
|
expect(flag).toBe(false);
|
|
});
|
|
|
|
runs(function () {
|
|
// With (the current version) of jQuery promises, resolution and
|
|
// resolution handlers will get called synchronously. However, if we
|
|
// move to a different promise implementation (e.g. Q) then resolution
|
|
// handlers will get called asynchronously. So, we check completion
|
|
// of the promise on a separate pass.
|
|
waitsForDone(promise, "The chain to complete");
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe("promisify", function () {
|
|
var testObj = {
|
|
someVal: 5,
|
|
succeeder: function (input, cb) {
|
|
cb(null, input, this.someVal);
|
|
},
|
|
failer: function (input, cb) {
|
|
cb("this is an error");
|
|
}
|
|
};
|
|
|
|
it("should resolve its returned promise when the errback is called with null err", function () {
|
|
Async.promisify(testObj, "succeeder", "myInput")
|
|
.then(function (input, someVal) {
|
|
expect(input).toBe("myInput");
|
|
expect(someVal).toBe(testObj.someVal);
|
|
}, function (err) {
|
|
expect("should not have called fail callback").toBe(false);
|
|
});
|
|
});
|
|
|
|
it("should reject its returned promise when the errback is called with an err", function () {
|
|
Async.promisify(testObj, "failer", "myInput")
|
|
.then(function (input, someVal) {
|
|
expect("should not have called success callback").toBe(false);
|
|
}, function (err) {
|
|
expect(err).toBe("this is an error");
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Async PromiseQueue", function () {
|
|
var queue, calledFns;
|
|
|
|
beforeEach(function () {
|
|
queue = new PromiseQueue();
|
|
calledFns = {};
|
|
});
|
|
|
|
function makeFn(id, resolveNow, rejectNow) {
|
|
var result = new $.Deferred();
|
|
return {
|
|
fn: function () {
|
|
calledFns[id] = true;
|
|
if (rejectNow) {
|
|
result.reject();
|
|
} else if (resolveNow) {
|
|
result.resolve();
|
|
}
|
|
return result.promise();
|
|
},
|
|
deferred: result
|
|
};
|
|
}
|
|
|
|
it("should immediately execute a function when the queue is empty", function () {
|
|
var fnInfo = makeFn("one");
|
|
|
|
queue.add(fnInfo.fn);
|
|
expect(calledFns.one).toBe(true);
|
|
|
|
fnInfo.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
});
|
|
|
|
it("should wait to execute the second added function", function () {
|
|
var fnInfo1 = makeFn("one"),
|
|
fnInfo2 = makeFn("two");
|
|
|
|
queue.add(fnInfo1.fn);
|
|
expect(calledFns.one).toBe(true);
|
|
|
|
queue.add(fnInfo2.fn);
|
|
expect(calledFns.two).toBeUndefined();
|
|
|
|
fnInfo1.deferred.resolve();
|
|
expect(calledFns.two).toBe(true);
|
|
|
|
fnInfo2.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
});
|
|
|
|
it("should properly handle the case where the first function completes synchronously", function () {
|
|
var fnInfo1 = makeFn("one", true),
|
|
fnInfo2 = makeFn("two");
|
|
|
|
queue.add(fnInfo1.fn);
|
|
expect(calledFns.one).toBe(true);
|
|
|
|
queue.add(fnInfo2.fn);
|
|
expect(calledFns.two).toBe(true);
|
|
|
|
fnInfo2.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
});
|
|
|
|
it("should sequentially execute a second and third function if they're both added while the first is executing", function () {
|
|
var fnInfo1 = makeFn("one"),
|
|
fnInfo2 = makeFn("two"),
|
|
fnInfo3 = makeFn("three");
|
|
|
|
queue.add(fnInfo1.fn);
|
|
expect(calledFns.one).toBe(true);
|
|
|
|
queue.add(fnInfo2.fn);
|
|
queue.add(fnInfo3.fn);
|
|
expect(calledFns.two).toBeUndefined();
|
|
expect(calledFns.three).toBeUndefined();
|
|
|
|
fnInfo1.deferred.resolve();
|
|
expect(calledFns.two).toBe(true);
|
|
expect(calledFns.three).toBeUndefined();
|
|
|
|
fnInfo2.deferred.resolve();
|
|
expect(calledFns.three).toBe(true);
|
|
|
|
fnInfo3.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
});
|
|
|
|
it("should sequentially execute a second and third function if the third is added while the second is executing", function () {
|
|
var fnInfo1 = makeFn("one"),
|
|
fnInfo2 = makeFn("two"),
|
|
fnInfo3 = makeFn("three");
|
|
|
|
queue.add(fnInfo1.fn);
|
|
expect(calledFns.one).toBe(true);
|
|
|
|
queue.add(fnInfo2.fn);
|
|
expect(calledFns.two).toBeUndefined();
|
|
|
|
fnInfo1.deferred.resolve();
|
|
expect(calledFns.two).toBe(true);
|
|
|
|
queue.add(fnInfo3.fn);
|
|
expect(calledFns.three).toBeUndefined();
|
|
|
|
fnInfo2.deferred.resolve();
|
|
expect(calledFns.three).toBe(true);
|
|
|
|
fnInfo3.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
});
|
|
|
|
it("should execute a second function when added to the empty queue after the first function has completed", function () {
|
|
var fnInfo1 = makeFn("one"),
|
|
fnInfo2 = makeFn("two");
|
|
|
|
queue.add(fnInfo1.fn);
|
|
expect(calledFns.one).toBe(true);
|
|
|
|
fnInfo1.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
|
|
queue.add(fnInfo2.fn);
|
|
expect(calledFns.two).toBe(true);
|
|
|
|
fnInfo2.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
});
|
|
|
|
it("should execute the second function if the first function is rejected", function () {
|
|
var fnInfo1 = makeFn("one"),
|
|
fnInfo2 = makeFn("two");
|
|
|
|
queue.add(fnInfo1.fn);
|
|
expect(calledFns.one).toBe(true);
|
|
|
|
queue.add(fnInfo2.fn);
|
|
expect(calledFns.two).toBeUndefined();
|
|
|
|
fnInfo1.deferred.reject();
|
|
expect(calledFns.two).toBe(true);
|
|
|
|
fnInfo2.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
});
|
|
|
|
it("should execute the third function after first and second functions are rejected", function () {
|
|
var fnInfo1 = makeFn("one"),
|
|
fnInfo2 = makeFn("two"),
|
|
fnInfo3 = makeFn("three");
|
|
|
|
queue.add(fnInfo1.fn);
|
|
expect(calledFns.one).toBe(true);
|
|
|
|
queue.add(fnInfo2.fn);
|
|
queue.add(fnInfo3.fn);
|
|
expect(calledFns.two).toBeUndefined();
|
|
expect(calledFns.three).toBeUndefined();
|
|
|
|
fnInfo1.deferred.reject();
|
|
expect(calledFns.two).toBe(true);
|
|
|
|
fnInfo2.deferred.reject();
|
|
expect(calledFns.three).toBe(true);
|
|
|
|
fnInfo3.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
});
|
|
|
|
it("should execute the second function after the already-rejected first function is added to the queue", function () {
|
|
var fnInfo1 = makeFn("one", false, true),
|
|
fnInfo2 = makeFn("two");
|
|
|
|
queue.add(fnInfo1.fn);
|
|
expect(calledFns.one).toBe(true);
|
|
|
|
queue.add(fnInfo2.fn);
|
|
expect(calledFns.two).toBe(true);
|
|
|
|
fnInfo2.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
});
|
|
|
|
it("should be able to run two queues simultaneously without clashing", function () {
|
|
var queue2 = new PromiseQueue(),
|
|
q1FnInfo1 = makeFn("one"),
|
|
q1FnInfo2 = makeFn("two"),
|
|
q2FnInfo3 = makeFn("three"),
|
|
q2FnInfo4 = makeFn("four");
|
|
|
|
//queue one
|
|
queue.add(q1FnInfo1.fn);
|
|
queue.add(q1FnInfo2.fn);
|
|
expect(calledFns.one).toBe(true);
|
|
expect(calledFns.two).toBeUndefined();
|
|
//queue one should have one in _queue,
|
|
//queue two should have zero in _queue
|
|
expect(queue._queue.length).toBe(1);
|
|
expect(queue2._queue.length).toBe(0);
|
|
|
|
//queue two
|
|
queue2.add(q2FnInfo3.fn);
|
|
queue2.add(q2FnInfo4.fn);
|
|
expect(calledFns.three).toBe(true);
|
|
expect(calledFns.four).toBeUndefined();
|
|
//queue one and two should have one in _queue
|
|
expect(queue._queue.length).toBe(1);
|
|
expect(queue2._queue.length).toBe(1);
|
|
|
|
q1FnInfo1.deferred.resolve();
|
|
expect(calledFns.two).toBe(true);
|
|
expect(queue._queue.length).toBe(0);
|
|
|
|
q1FnInfo2.deferred.resolve();
|
|
expect(queue._queue.length).toBe(0);
|
|
expect(queue._curPromise).toBe(null);
|
|
|
|
q2FnInfo3.deferred.resolve();
|
|
expect(calledFns.three).toBe(true);
|
|
expect(queue2._queue.length).toBe(0);
|
|
|
|
q2FnInfo4.deferred.resolve();
|
|
expect(queue2._queue.length).toBe(0);
|
|
expect(queue2._curPromise).toBe(null);
|
|
});
|
|
});
|
|
});
|
|
});
|