Revision control

Copy as Markdown

Other Tools

/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { AccountCreationUtils } = ChromeUtils.importESModule(
);
const { BrowserTestUtils } = ChromeUtils.importESModule(
);
const { TestUtils } = ChromeUtils.importESModule(
);
add_task(async function test_promiseFirstSuccessful_success() {
const firstPromise = Promise.withResolvers();
const secondPromise = Promise.withResolvers();
const thirdPromise = Promise.withResolvers();
const queue = [
firstPromise.promise,
secondPromise.promise,
thirdPromise.promise,
];
const controller = new AbortController();
const promise = AccountCreationUtils.promiseFirstSuccessful(
queue,
controller
);
firstPromise.reject(new Error("first failed"));
secondPromise.resolve("second ftw");
// never touching the third promise.
const result = await promise;
Assert.deepEqual(
result,
{ value: "second ftw", index: 1 },
"Should get the result from the second promise"
);
Assert.ok(controller.signal.aborted, "Should have aborted the signal");
});
add_task(async function test_promiseFirstSuccessful_allFailed() {
const firstPromise = Promise.withResolvers();
const secondPromise = Promise.withResolvers();
const queue = [firstPromise.promise, secondPromise.promise];
const controller = new AbortController();
const promise = AccountCreationUtils.promiseFirstSuccessful(
queue,
controller
);
const firstError = new Error("first");
const secondError = new Error("second");
secondPromise.reject(secondError);
firstPromise.reject(firstError);
await Assert.rejects(
promise,
error => error.errors[0] === firstError && error.errors[1] === secondError,
"Should reject with an aggregate error of all rejections"
);
});
add_task(async function test_promiseFirstSuccessful_cleanupRemainingFailures() {
const firstPromise = Promise.withResolvers();
const secondPromise = Promise.withResolvers();
const thirdPromise = Promise.withResolvers();
const queue = [
firstPromise.promise,
secondPromise.promise,
thirdPromise.promise,
];
const controller = new AbortController();
const promise = AccountCreationUtils.promiseFirstSuccessful(
queue,
controller
);
firstPromise.reject(new Error("first failed"));
// This failure could be unhandled if not explicitly cleaned up after handling
// the resolution of the second promise.
thirdPromise.reject(new Error("third failed"));
secondPromise.resolve("second ftw");
const result = await promise;
Assert.deepEqual(
result,
{ value: "second ftw", index: 1 },
"Should get the result from the second promise"
);
Assert.ok(controller.signal.aborted, "Should have aborted the signal");
});
add_task(async function test_abortSignalTimeout() {
const start = Date.now();
const signal = AccountCreationUtils.abortSignalTimeout(100);
await BrowserTestUtils.waitForEvent(signal, "abort");
const end = Date.now();
Assert.ok(signal.aborted, "Should have aborted signal");
Assert.ok(Error.isError(signal.reason), "Should abort with an error");
Assert.equal(
signal.reason.message,
"100ms timeout",
"Error message should contain the timeout"
);
Assert.greaterOrEqual(
end,
start + 100,
"Should have aborted after the timeout expired"
);
});
add_task(async function test_abortableTimeout() {
const abortController = new AbortController();
const start = Date.now();
await AccountCreationUtils.abortableTimeout(10, abortController.signal);
Assert.greaterOrEqual(
Date.now(),
start + 10,
"At least the amount of time specified for the timeout should have passed"
);
const abortError = new Error("test abort");
// eslint-disable-next-line mozilla/rejects-requires-await
const rejection = Assert.rejects(
AccountCreationUtils.abortableTimeout(100, abortController.signal),
error => error == abortError,
"Should abort from the signal"
);
abortController.abort(abortError);
await rejection;
});
add_task(function test_deepCopy() {
const primitiveValues = [
undefined,
null,
"string",
0,
1,
NaN,
true,
() => {},
];
for (const value of primitiveValues) {
// Special case for NaN since NaN != NaN
if (Number.isNaN(value)) {
Assert.ok(Number.isNaN(value), "Should return NaN when passed NaN");
} else {
Assert.equal(
AccountCreationUtils.deepCopy(value),
value,
"Should return the same value as passed in"
);
}
}
const obj = {
foo: "bar",
theAnswer: 42,
isUseful: false,
explicitlyUndefined: undefined,
justNull: null,
method: () => {},
anArray: [1, "???", () => "profit"],
subObject: {
evenMore: true,
},
anInstance: new Error("test"),
};
const clone = AccountCreationUtils.deepCopy(obj);
Assert.deepEqual(
clone,
obj,
"deepCopy should create an identical looking clone"
);
Assert.notStrictEqual(
clone,
obj,
"deepCopy should return a different object"
);
Assert.notStrictEqual(
clone.anArray,
obj.anArray,
"deepCopy should clone the array"
);
Assert.notStrictEqual(
clone.subObject,
obj.subObject,
"deepCopy should clone nested objects"
);
Assert.throws(
() => AccountCreationUtils.deepCopy(Symbol("test")),
error =>
Error.isError(error) &&
error.message == "can't copy objects of type symbol yet",
"Should throw when trying to copy a symbol"
);
});
add_task(async function test_exceptions() {
const exceptionTypes = [
AccountCreationUtils.CancelledException,
AccountCreationUtils.NotReached,
AccountCreationUtils.UserCancelledException,
];
for (const Exception of exceptionTypes) {
const instance = new Exception("test");
Assert.ok(Error.isError(instance));
Assert.equal(instance.message, "test", "Message param is passed on");
}
Assert.equal(
Object.getPrototypeOf(AccountCreationUtils.UserCancelledException),
AccountCreationUtils.CancelledException,
"UserCancelledException should extend CancelledException"
);
Assert.ok(
new AccountCreationUtils.UserCancelledException().message,
"UserCancelledException should have a message by default"
);
info("Checking that NotReached logs the error...");
const consolePromise = TestUtils.consoleMessageObserved(
message => message?.wrappedJSObject?.arguments[0]?.message == "foo"
);
new AccountCreationUtils.NotReached("foo");
await consolePromise;
});
add_task(function test_assert() {
Assert.throws(
() => AccountCreationUtils.assert(false, "foo"),
error => Error.isError(error) && error.message == "foo",
"Should throw error with the given message if assertion fails"
);
Assert.throws(
() => AccountCreationUtils.assert(false),
error => Error.isError(error) && Boolean(error.message),
"Should throw an error with a generic message if no assertion message is specified"
);
info("Making sure assertions that pass don't throw...");
AccountCreationUtils.assert(true, "This shouldn't be thrown");
});
add_task(function test_standardPorts() {
Assert.ok(
Array.isArray(AccountCreationUtils.standardPorts),
"Standard ports should be an array"
);
for (const port of AccountCreationUtils.standardPorts) {
Assert.equal(typeof port, "number", `Port ${port} should be a number`);
Assert.ok(Number.isInteger(port), `Port ${port} should be an integer`);
Assert.greater(port, 0, `Port ${port} should be a positive number`);
Assert.lessOrEqual(
port,
65535,
`Port ${port} should fit in a 16 bit integer`
);
}
});