491 lines
13 KiB
JavaScript
491 lines
13 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
// Test cases borrowed and adapted from:
|
|
// https://github.com/joyent/node/blob/6101eb184db77d0b11eb96e48744e57ecce4b73d/test/simple/test-assert.js
|
|
// MIT license: http://opensource.org/licenses/MIT
|
|
|
|
var { Assert } = ChromeUtils.importESModule(
|
|
"resource://testing-common/Assert.sys.mjs"
|
|
);
|
|
|
|
add_task(function test_basic_asserts() {
|
|
let assert = new Assert();
|
|
|
|
function makeBlock(f, ...args) {
|
|
return function () {
|
|
return f.apply(assert, args);
|
|
};
|
|
}
|
|
|
|
function protoCtrChain(o) {
|
|
let result = [];
|
|
while ((o = o.__proto__)) {
|
|
result.push(o.constructor);
|
|
}
|
|
return result.join();
|
|
}
|
|
|
|
function indirectInstanceOf(obj, cls) {
|
|
if (obj instanceof cls) {
|
|
return true;
|
|
}
|
|
let clsChain = protoCtrChain(cls.prototype);
|
|
let objChain = protoCtrChain(obj);
|
|
return objChain.slice(-clsChain.length) === clsChain;
|
|
}
|
|
|
|
assert.ok(
|
|
indirectInstanceOf(Assert.AssertionError.prototype, Error),
|
|
"Assert.AssertionError instanceof Error"
|
|
);
|
|
|
|
assert.throws(
|
|
makeBlock(assert.ok, false),
|
|
Assert.AssertionError,
|
|
"ok(false)"
|
|
);
|
|
|
|
assert.ok(true, "ok(true)");
|
|
|
|
assert.ok("test", "ok('test')");
|
|
|
|
assert.throws(
|
|
makeBlock(assert.equal, true, false),
|
|
Assert.AssertionError,
|
|
"equal"
|
|
);
|
|
|
|
assert.equal(null, null, "equal");
|
|
|
|
assert.equal(undefined, undefined, "equal");
|
|
|
|
assert.equal(null, undefined, "equal");
|
|
|
|
assert.equal(true, true, "equal");
|
|
|
|
assert.notEqual(true, false, "notEqual");
|
|
|
|
assert.throws(
|
|
makeBlock(assert.notEqual, true, true),
|
|
Assert.AssertionError,
|
|
"notEqual"
|
|
);
|
|
|
|
assert.throws(
|
|
makeBlock(assert.strictEqual, 2, "2"),
|
|
Assert.AssertionError,
|
|
"strictEqual"
|
|
);
|
|
|
|
assert.throws(
|
|
makeBlock(assert.strictEqual, null, undefined),
|
|
Assert.AssertionError,
|
|
"strictEqual"
|
|
);
|
|
|
|
assert.notStrictEqual(2, "2", "notStrictEqual");
|
|
|
|
// deepEquals joy!
|
|
// 7.2
|
|
assert.deepEqual(
|
|
new Date(2000, 3, 14),
|
|
new Date(2000, 3, 14),
|
|
"deepEqual date"
|
|
);
|
|
assert.deepEqual(new Date(NaN), new Date(NaN), "deepEqual invalid dates");
|
|
|
|
assert.throws(
|
|
makeBlock(assert.deepEqual, new Date(), new Date(2000, 3, 14)),
|
|
Assert.AssertionError,
|
|
"deepEqual date"
|
|
);
|
|
|
|
// 7.3
|
|
assert.deepEqual(/a/, /a/);
|
|
assert.deepEqual(/a/g, /a/g);
|
|
assert.deepEqual(/a/i, /a/i);
|
|
assert.deepEqual(/a/m, /a/m);
|
|
assert.deepEqual(/a/gim, /a/gim);
|
|
assert.throws(makeBlock(assert.deepEqual, /ab/, /a/), Assert.AssertionError);
|
|
assert.throws(makeBlock(assert.deepEqual, /a/g, /a/), Assert.AssertionError);
|
|
assert.throws(makeBlock(assert.deepEqual, /a/i, /a/), Assert.AssertionError);
|
|
assert.throws(makeBlock(assert.deepEqual, /a/m, /a/), Assert.AssertionError);
|
|
assert.throws(
|
|
makeBlock(assert.deepEqual, /a/gim, /a/im),
|
|
Assert.AssertionError
|
|
);
|
|
|
|
let re1 = /a/;
|
|
re1.lastIndex = 3;
|
|
assert.throws(makeBlock(assert.deepEqual, re1, /a/), Assert.AssertionError);
|
|
|
|
// 7.4
|
|
assert.deepEqual(4, "4", "deepEqual == check");
|
|
assert.deepEqual(true, 1, "deepEqual == check");
|
|
assert.throws(
|
|
makeBlock(assert.deepEqual, 4, "5"),
|
|
Assert.AssertionError,
|
|
"deepEqual == check"
|
|
);
|
|
|
|
// 7.5
|
|
// having the same number of owned properties && the same set of keys
|
|
assert.deepEqual({ a: 4 }, { a: 4 });
|
|
assert.deepEqual({ a: 4, b: "2" }, { a: 4, b: "2" });
|
|
assert.deepEqual([4], ["4"]);
|
|
assert.throws(
|
|
makeBlock(assert.deepEqual, { a: 4 }, { a: 4, b: true }),
|
|
Assert.AssertionError
|
|
);
|
|
assert.deepEqual(["a"], { 0: "a" });
|
|
|
|
let a1 = [1, 2, 3];
|
|
let a2 = [1, 2, 3];
|
|
a1.a = "test";
|
|
a1.b = true;
|
|
a2.b = true;
|
|
a2.a = "test";
|
|
assert.throws(
|
|
makeBlock(assert.deepEqual, Object.keys(a1), Object.keys(a2)),
|
|
Assert.AssertionError
|
|
);
|
|
assert.deepEqual(a1, a2);
|
|
|
|
let nbRoot = {
|
|
toString() {
|
|
return this.first + " " + this.last;
|
|
},
|
|
};
|
|
|
|
function nameBuilder(first, last) {
|
|
this.first = first;
|
|
this.last = last;
|
|
return this;
|
|
}
|
|
nameBuilder.prototype = nbRoot;
|
|
|
|
function nameBuilder2(first, last) {
|
|
this.first = first;
|
|
this.last = last;
|
|
return this;
|
|
}
|
|
nameBuilder2.prototype = nbRoot;
|
|
|
|
let nb1 = new nameBuilder("Ryan", "Dahl");
|
|
let nb2 = new nameBuilder2("Ryan", "Dahl");
|
|
|
|
assert.deepEqual(nb1, nb2);
|
|
|
|
nameBuilder2.prototype = Object;
|
|
nb2 = new nameBuilder2("Ryan", "Dahl");
|
|
assert.throws(makeBlock(assert.deepEqual, nb1, nb2), Assert.AssertionError);
|
|
|
|
// String literal + object
|
|
assert.throws(makeBlock(assert.deepEqual, "a", {}), Assert.AssertionError);
|
|
|
|
// Testing the throwing
|
|
function thrower(errorConstructor) {
|
|
throw new errorConstructor("test");
|
|
}
|
|
makeBlock(thrower, Assert.AssertionError);
|
|
makeBlock(thrower, Assert.AssertionError);
|
|
|
|
// the basic calls work
|
|
assert.throws(
|
|
makeBlock(thrower, Assert.AssertionError),
|
|
Assert.AssertionError,
|
|
"message"
|
|
);
|
|
assert.throws(
|
|
makeBlock(thrower, Assert.AssertionError),
|
|
Assert.AssertionError
|
|
);
|
|
assert.throws(
|
|
makeBlock(thrower, Assert.AssertionError),
|
|
Assert.AssertionError
|
|
);
|
|
|
|
// if not passing an error, catch all.
|
|
assert.throws(makeBlock(thrower, TypeError), TypeError);
|
|
|
|
// when passing a type, only catch errors of the appropriate type
|
|
let threw = false;
|
|
try {
|
|
assert.throws(makeBlock(thrower, TypeError), Assert.AssertionError);
|
|
} catch (e) {
|
|
threw = true;
|
|
assert.ok(e instanceof TypeError, "type");
|
|
}
|
|
assert.equal(
|
|
true,
|
|
threw,
|
|
"Assert.throws with an explicit error is eating extra errors",
|
|
Assert.AssertionError
|
|
);
|
|
threw = false;
|
|
|
|
function ifError(err) {
|
|
if (err) {
|
|
throw err;
|
|
}
|
|
}
|
|
assert.throws(function () {
|
|
ifError(new Error("test error"));
|
|
}, /test error/);
|
|
|
|
// make sure that validating using constructor really works
|
|
threw = false;
|
|
try {
|
|
assert.throws(function () {
|
|
throw new Error({});
|
|
}, Array);
|
|
} catch (e) {
|
|
threw = true;
|
|
}
|
|
assert.ok(threw, "wrong constructor validation");
|
|
|
|
// use a RegExp to validate error message
|
|
assert.throws(makeBlock(thrower, TypeError), /test/);
|
|
|
|
// use a fn to validate error object
|
|
assert.throws(makeBlock(thrower, TypeError), function (err) {
|
|
if (err instanceof TypeError && /test/.test(err)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
// do the same with an arrow function
|
|
assert.throws(makeBlock(thrower, TypeError), err => {
|
|
if (err instanceof TypeError && /test/.test(err)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
function testAssertionMessage(actual, expected) {
|
|
try {
|
|
assert.equal(actual, "");
|
|
} catch (e) {
|
|
assert.equal(
|
|
e.toString(),
|
|
["AssertionError:", expected, "==", '""'].join(" ")
|
|
);
|
|
}
|
|
}
|
|
testAssertionMessage(undefined, '"undefined"');
|
|
testAssertionMessage(null, "null");
|
|
testAssertionMessage(true, "true");
|
|
testAssertionMessage(false, "false");
|
|
testAssertionMessage(0, "0");
|
|
testAssertionMessage(100, "100");
|
|
testAssertionMessage(NaN, '"NaN"');
|
|
testAssertionMessage(Infinity, '"Infinity"');
|
|
testAssertionMessage(-Infinity, '"-Infinity"');
|
|
testAssertionMessage("", '""');
|
|
testAssertionMessage("foo", '"foo"');
|
|
testAssertionMessage([], "[]");
|
|
testAssertionMessage([1, 2, 3], "[1,2,3]");
|
|
testAssertionMessage(/a/, '"/a/"');
|
|
testAssertionMessage(/abc/gim, '"/abc/gim"');
|
|
testAssertionMessage(function f() {}, '"function f() {}"');
|
|
testAssertionMessage({}, "{}");
|
|
testAssertionMessage({ a: undefined, b: null }, '{"a":"undefined","b":null}');
|
|
testAssertionMessage(
|
|
{ a: NaN, b: Infinity, c: -Infinity },
|
|
'{"a":"NaN","b":"Infinity","c":"-Infinity"}'
|
|
);
|
|
|
|
// https://github.com/joyent/node/issues/2893
|
|
try {
|
|
assert.throws(function () {
|
|
ifError(null);
|
|
});
|
|
} catch (e) {
|
|
threw = true;
|
|
assert.equal(
|
|
e.message,
|
|
"Error: The 'expected' argument was not supplied to Assert.throws() - false == true"
|
|
);
|
|
}
|
|
assert.ok(threw);
|
|
|
|
// https://github.com/joyent/node/issues/5292
|
|
try {
|
|
assert.equal(1, 2);
|
|
} catch (e) {
|
|
assert.equal(e.toString().split("\n")[0], "AssertionError: 1 == 2");
|
|
}
|
|
|
|
try {
|
|
assert.equal(1, 2, "oh no");
|
|
} catch (e) {
|
|
assert.equal(e.toString().split("\n")[0], "AssertionError: oh no - 1 == 2");
|
|
}
|
|
|
|
// Need to JSON.stringify so that their length is > 128 characters.
|
|
let longArray0 = Array.from(Array(50), (v, i) => i);
|
|
let longArray1 = longArray0.concat([51]);
|
|
try {
|
|
assert.deepEqual(longArray0, longArray1);
|
|
} catch (e) {
|
|
let message = e.toString();
|
|
// Just check that they're both entirely present in the message
|
|
assert.ok(message.includes(JSON.stringify(longArray0)));
|
|
assert.ok(message.includes(JSON.stringify(longArray1)));
|
|
}
|
|
|
|
// Test XPCShell-test integration:
|
|
ok(true, "OK, this went well");
|
|
deepEqual(/a/g, /a/g, "deep equal should work on RegExp");
|
|
deepEqual(/a/gim, /a/gim, "deep equal should work on RegExp");
|
|
deepEqual(
|
|
{ a: 4, b: "1" },
|
|
{ b: "1", a: 4 },
|
|
"deep equal should work on regular Object"
|
|
);
|
|
deepEqual(a1, a2, "deep equal should work on Array with Object properties");
|
|
|
|
// Test robustness of reporting:
|
|
equal(
|
|
new Assert.AssertionError({
|
|
actual: {
|
|
toJSON() {
|
|
throw new Error("bam!");
|
|
},
|
|
},
|
|
expected: "foo",
|
|
operator: "=",
|
|
}).message,
|
|
'[object Object] = "foo"'
|
|
);
|
|
|
|
let message;
|
|
assert.greater(3, 2);
|
|
try {
|
|
assert.greater(2, 2);
|
|
} catch (e) {
|
|
message = e.toString().split("\n")[0];
|
|
}
|
|
assert.equal(message, "AssertionError: 2 > 2");
|
|
|
|
assert.greaterOrEqual(2, 2);
|
|
try {
|
|
assert.greaterOrEqual(1, 2);
|
|
} catch (e) {
|
|
message = e.toString().split("\n")[0];
|
|
}
|
|
assert.equal(message, "AssertionError: 1 >= 2");
|
|
|
|
assert.less(1, 2);
|
|
assert.throws(
|
|
() => assert.less(2, 2),
|
|
e => e == "AssertionError: 2 < 2"
|
|
);
|
|
|
|
assert.lessOrEqual(2, 2);
|
|
assert.throws(
|
|
() => assert.lessOrEqual(2, 1),
|
|
e => e == "AssertionError: 2 <= 1"
|
|
);
|
|
|
|
assert.throws(
|
|
() => assert.greater(NaN, 0),
|
|
e => e == "AssertionError: 'NaN' is not a number or date."
|
|
);
|
|
|
|
assert.throws(
|
|
() => assert.greater(0, NaN),
|
|
e => e == "AssertionError: 'NaN' is not a number or date."
|
|
);
|
|
|
|
let now = new Date();
|
|
let firefoxReleaseDate = new Date("2004-11-09");
|
|
assert.less(firefoxReleaseDate, now);
|
|
assert.throws(
|
|
() => assert.less(now, now),
|
|
e => e == `AssertionError: "${now.toJSON()}" < "${now.toJSON()}"`
|
|
);
|
|
|
|
assert.lessOrEqual(now, now);
|
|
assert.greaterOrEqual(now, now);
|
|
assert.throws(
|
|
() => assert.greaterOrEqual(firefoxReleaseDate, now),
|
|
e =>
|
|
e ==
|
|
`AssertionError: "${firefoxReleaseDate.toJSON()}" >= "${now.toJSON()}"`
|
|
);
|
|
|
|
// Invalid date:
|
|
assert.throws(
|
|
() => assert.greater(firefoxReleaseDate, new Date("invalid")),
|
|
e => e == "AssertionError: 'Invalid Date' is not a number or date."
|
|
);
|
|
|
|
/* ---- stringMatches ---- */
|
|
assert.stringMatches("hello world", /llo\s/);
|
|
assert.stringMatches("hello world", "llo\\s");
|
|
assert.throws(
|
|
() => assert.stringMatches("hello world", /foo/),
|
|
/^AssertionError: "hello world" matches "\/foo\/"/
|
|
);
|
|
assert.throws(
|
|
() => assert.stringMatches(5, /foo/),
|
|
/^AssertionError: Expected a string for lhs, but "5" isn't a string./
|
|
);
|
|
assert.throws(
|
|
() => assert.stringMatches("foo bar", "+"),
|
|
/^AssertionError: Expected a valid regular expression for rhs, but "\+" isn't one./
|
|
);
|
|
|
|
/* ---- stringContains ---- */
|
|
assert.stringContains("hello world", "llo");
|
|
assert.throws(
|
|
() => assert.stringContains(5, "foo"),
|
|
/^AssertionError: Expected a string for both lhs and rhs, but either "5" or "foo" is not a string./
|
|
);
|
|
});
|
|
|
|
add_task(async function test_rejects() {
|
|
let assert = new Assert();
|
|
|
|
// A helper function to test failures.
|
|
async function checkRejectsFails(err, expected) {
|
|
try {
|
|
await assert.rejects(Promise.reject(err), expected);
|
|
ok(false, "should have thrown");
|
|
} catch (ex) {
|
|
deepEqual(ex, err, "Assert.rejects threw the original unexpected error");
|
|
}
|
|
}
|
|
|
|
// A "throwable" error that's not an actual Error().
|
|
let SomeErrorLikeThing = function () {};
|
|
|
|
// The actual tests...
|
|
|
|
// An explicit error object:
|
|
// An instance to check against.
|
|
await assert.rejects(Promise.reject(new Error("oh no")), Error, "rejected");
|
|
// A regex to match against the message.
|
|
await assert.rejects(Promise.reject(new Error("oh no")), /oh no/, "rejected");
|
|
|
|
// Failure cases:
|
|
// An instance to check against that doesn't match.
|
|
await checkRejectsFails(new Error("something else"), SomeErrorLikeThing);
|
|
// A regex that doesn't match.
|
|
await checkRejectsFails(new Error("something else"), /oh no/);
|
|
|
|
// Check simple string messages.
|
|
await assert.rejects(Promise.reject("oh no"), /oh no/, "rejected");
|
|
// Wrong message.
|
|
await checkRejectsFails("something else", /oh no/);
|
|
|
|
// A non-rejection should also be an assertion failure:
|
|
try {
|
|
await assert.rejects(Promise.resolve(), /./, "ReSoLvEd");
|
|
ok(false, "should have rejected");
|
|
} catch (ex) {
|
|
deepEqual(ex.message, "Missing expected exception ReSoLvEd");
|
|
}
|
|
});
|