зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1688669
- Add a helper to assert property values in objects r=canaltinova
Differential Revision: https://phabricator.services.mozilla.com/D102939
This commit is contained in:
Родитель
761a4721d8
Коммит
fb405c1684
|
@ -2,6 +2,8 @@
|
|||
* 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/. */
|
||||
|
||||
/* globals Assert */
|
||||
|
||||
/**
|
||||
* This file contains utilities that can be shared between xpcshell tests and mochitests.
|
||||
*/
|
||||
|
@ -218,3 +220,175 @@ function getSchema(profile, name) {
|
|||
}
|
||||
throw new Error(`Could not find a schema for "${name}".`);
|
||||
}
|
||||
|
||||
/** ------ Assertions helper ------ */
|
||||
/**
|
||||
* This assert helper function makes it easy to check a lot of properties in an
|
||||
* object. We augment Assert.jsm to make it easier to use.
|
||||
*/
|
||||
Object.assign(Assert, {
|
||||
/*
|
||||
* It checks if the properties on the right are all present in the object on
|
||||
* the left. Note that the object might still have other properties (see
|
||||
* objectContainsOnly below if you want the stricter form).
|
||||
*
|
||||
* The basic form does basic equality on each expected property:
|
||||
*
|
||||
* Assert.objectContains(fixture, {
|
||||
* foo: "foo",
|
||||
* bar: 1,
|
||||
* baz: true,
|
||||
* });
|
||||
*
|
||||
* But it also has a more powerful form with expectations. The available
|
||||
* expectations are:
|
||||
* - any(): this only checks for the existence of the property, not its value
|
||||
* - number(), string(), boolean(), bigint(), function(), symbol(), object():
|
||||
* this checks if the value is of this type
|
||||
* - objectContains(expected): this applies Assert.objectContains()
|
||||
* recursively on this property.
|
||||
* - stringContains(needle): this checks if the expected value is included in
|
||||
* the property value.
|
||||
* - stringMatches(regexp): this checks if the property value matches this
|
||||
* regexp. The regexp can be passed as a string, to be dynamically built.
|
||||
*
|
||||
* example:
|
||||
*
|
||||
* Assert.objectContains(fixture, {
|
||||
* name: Expect.stringMatches(`Load \\d+:.*${url}`),
|
||||
* data: Expect.objectContains({
|
||||
* status: "STATUS_STOP",
|
||||
* URI: Expect.stringContains("https://"),
|
||||
* requestMethod: "GET",
|
||||
* contentType: Expect.string(),
|
||||
* startTime: Expect.number(),
|
||||
* cached: Expect.boolean(),
|
||||
* }),
|
||||
* });
|
||||
*
|
||||
* Each expectation will translate into one or more Assert call. Therefore if
|
||||
* one expectation fails, this will be clearly visible in the test output.
|
||||
*
|
||||
* Expectations can also be normal functions, for example:
|
||||
*
|
||||
* Assert.objectContains(fixture, {
|
||||
* number: value => Assert.greater(value, 5)
|
||||
* });
|
||||
*
|
||||
* Note that you'll need to use Assert inside this function.
|
||||
*/
|
||||
objectContains(object, properties) {
|
||||
// Basic tests: we don't want to run other assertions if these tests fail.
|
||||
if (typeof object !== "object") {
|
||||
this.ok(
|
||||
false,
|
||||
`The first parameter should be an object, but found: ${object}.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof properties !== "object") {
|
||||
this.ok(
|
||||
false,
|
||||
`The second parameter should be an object, but found: ${properties}.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(properties)) {
|
||||
const expected = properties[key];
|
||||
if (!(key in object)) {
|
||||
this.ok(false, `The object should contain the property ${key}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof expected === "function") {
|
||||
// This is a function, so let's call it.
|
||||
expected(
|
||||
object[key],
|
||||
`The object should contain the property "${key}" with an expected value and type.`
|
||||
);
|
||||
} else {
|
||||
// Otherwise, we check for equality.
|
||||
this.equal(
|
||||
object[key],
|
||||
properties[key],
|
||||
`The object should contain the property "${key}" with an expected value.`
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This is very similar to the previous `objectContains`, but this also looks
|
||||
* at the number of the objects' properties. Thus this will fail if the
|
||||
* objects don't have the same properties exactly.
|
||||
*/
|
||||
objectContainsOnly(object, properties) {
|
||||
// Basic tests: we don't want to run other assertions if these tests fail.
|
||||
if (typeof object !== "object") {
|
||||
this.ok(
|
||||
false,
|
||||
`The first parameter should be an object but found: ${object}.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof properties !== "object") {
|
||||
this.ok(
|
||||
false,
|
||||
`The second parameter should be an object but found: ${properties}.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.equal(
|
||||
Object.keys(object).length,
|
||||
Object.keys(properties).length,
|
||||
"The 2 objects should have the same number of properties."
|
||||
);
|
||||
this.objectContains(object, properties);
|
||||
},
|
||||
});
|
||||
|
||||
const Expect = {
|
||||
any: () => actual => {} /* We don't check anything more than the presence of this property. */,
|
||||
};
|
||||
|
||||
/* These functions are part of the Assert object, and we want to reuse them. */
|
||||
[
|
||||
"stringContains",
|
||||
"stringMatches",
|
||||
"objectContains",
|
||||
"objectContainsOnly",
|
||||
].forEach(
|
||||
assertChecker =>
|
||||
(Expect[assertChecker] = expected => (actual, ...moreArgs) =>
|
||||
Assert[assertChecker](actual, expected, ...moreArgs))
|
||||
);
|
||||
|
||||
/* These functions will only check for the type. */
|
||||
[
|
||||
"number",
|
||||
"string",
|
||||
"boolean",
|
||||
"bigint",
|
||||
"symbol",
|
||||
"object",
|
||||
"function",
|
||||
].forEach(type => (Expect[type] = makeTypeChecker(type)));
|
||||
|
||||
function makeTypeChecker(type) {
|
||||
return (...unexpectedArgs) => {
|
||||
if (unexpectedArgs.length) {
|
||||
throw new Error(
|
||||
"Type checkers expectations aren't expecting any argument."
|
||||
);
|
||||
}
|
||||
return (actual, message) => {
|
||||
const isCorrect = typeof actual === type;
|
||||
Assert.report(!isCorrect, actual, type, message, "has type");
|
||||
};
|
||||
};
|
||||
}
|
||||
/* ------ End of assertion helper ------ */
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
add_task(function setup() {
|
||||
// With the default reporter, an assertion doesn't throw if it fails, it
|
||||
// merely report the result to the reporter and then go on. But in this test
|
||||
// we want that a failure really throws, so that we can actually assert that
|
||||
// it throws in case of failures!
|
||||
// That's why we disable the default repoter here.
|
||||
// I noticed that this line needs to be in an add_task (or possibly run_test)
|
||||
// function. If put outside this will crash the test.
|
||||
Assert.setReporter(null);
|
||||
});
|
||||
|
||||
add_task(function test_objectContains() {
|
||||
const fixture = {
|
||||
foo: "foo",
|
||||
bar: "bar",
|
||||
};
|
||||
|
||||
Assert.objectContains(fixture, { foo: "foo" }, "Matches one property value");
|
||||
Assert.objectContains(
|
||||
fixture,
|
||||
{ foo: "foo", bar: "bar" },
|
||||
"Matches both properties"
|
||||
);
|
||||
Assert.objectContainsOnly(
|
||||
fixture,
|
||||
{ foo: "foo", bar: "bar" },
|
||||
"Matches both properties"
|
||||
);
|
||||
Assert.throws(
|
||||
() => Assert.objectContainsOnly(fixture, { foo: "foo" }),
|
||||
/AssertionError/,
|
||||
"Fails if some properties are missing"
|
||||
);
|
||||
Assert.throws(
|
||||
() => Assert.objectContains(fixture, { foo: "bar" }),
|
||||
/AssertionError/,
|
||||
"Fails if the value for a present property is wrong"
|
||||
);
|
||||
Assert.throws(
|
||||
() => Assert.objectContains(fixture, { hello: "world" }),
|
||||
/AssertionError/,
|
||||
"Fails if an expected property is missing"
|
||||
);
|
||||
Assert.throws(
|
||||
() => Assert.objectContains(fixture, { foo: "foo", hello: "world" }),
|
||||
/AssertionError/,
|
||||
"Fails if some properties are present but others are missing"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(function test_objectContains_expectations() {
|
||||
const fixture = {
|
||||
foo: "foo",
|
||||
bar: "bar",
|
||||
num: 42,
|
||||
nested: {
|
||||
nestedFoo: "nestedFoo",
|
||||
nestedBar: "nestedBar",
|
||||
},
|
||||
};
|
||||
|
||||
Assert.objectContains(
|
||||
fixture,
|
||||
{
|
||||
foo: Expect.stringMatches(/^fo/),
|
||||
bar: Expect.stringContains("ar"),
|
||||
num: Expect.number(),
|
||||
nested: Expect.objectContainsOnly({
|
||||
nestedFoo: Expect.stringMatches(/[Ff]oo/),
|
||||
nestedBar: Expect.stringMatches(/[Bb]ar/),
|
||||
}),
|
||||
},
|
||||
"Supports expectations"
|
||||
);
|
||||
Assert.objectContainsOnly(
|
||||
fixture,
|
||||
{
|
||||
foo: Expect.stringMatches(/^fo/),
|
||||
bar: Expect.stringContains("ar"),
|
||||
num: Expect.number(),
|
||||
nested: Expect.objectContains({
|
||||
nestedFoo: Expect.stringMatches(/[Ff]oo/),
|
||||
}),
|
||||
},
|
||||
"Supports expectations"
|
||||
);
|
||||
|
||||
Assert.objectContains(fixture, {
|
||||
num: val => Assert.greater(val, 40),
|
||||
});
|
||||
|
||||
// Failed expectations
|
||||
Assert.throws(
|
||||
() =>
|
||||
Assert.objectContains(fixture, {
|
||||
foo: Expect.stringMatches(/bar/),
|
||||
}),
|
||||
/AssertionError/,
|
||||
"Expect.stringMatches shouldn't match when the value is unexpected"
|
||||
);
|
||||
Assert.throws(
|
||||
() =>
|
||||
Assert.objectContains(fixture, {
|
||||
foo: Expect.stringContains("bar"),
|
||||
}),
|
||||
/AssertionError/,
|
||||
"Expect.stringContains shouldn't match when the value is unexpected"
|
||||
);
|
||||
Assert.throws(
|
||||
() =>
|
||||
Assert.objectContains(fixture, {
|
||||
foo: Expect.number(),
|
||||
}),
|
||||
/AssertionError/,
|
||||
"Expect.number shouldn't match when the value isn't a number"
|
||||
);
|
||||
Assert.throws(
|
||||
() =>
|
||||
Assert.objectContains(fixture, {
|
||||
nested: Expect.objectContains({
|
||||
nestedFoo: "bar",
|
||||
}),
|
||||
}),
|
||||
/AssertionError/,
|
||||
"Expect.objectContains should throw when the value is unexpected"
|
||||
);
|
||||
|
||||
Assert.throws(
|
||||
() =>
|
||||
Assert.objectContains(fixture, {
|
||||
num: val => Assert.less(val, 40),
|
||||
}),
|
||||
/AssertionError/,
|
||||
"Expect.objectContains should throw when a function assertion fails"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(function test_type_expectations() {
|
||||
const fixture = {
|
||||
any: "foo",
|
||||
string: "foo",
|
||||
number: 42,
|
||||
boolean: true,
|
||||
bigint: 42n,
|
||||
symbol: Symbol("foo"),
|
||||
object: { foo: "foo" },
|
||||
function1() {},
|
||||
function2: () => {},
|
||||
};
|
||||
|
||||
Assert.objectContains(fixture, {
|
||||
any: Expect.any(),
|
||||
string: Expect.string(),
|
||||
number: Expect.number(),
|
||||
boolean: Expect.boolean(),
|
||||
bigint: Expect.bigint(),
|
||||
symbol: Expect.symbol(),
|
||||
object: Expect.object(),
|
||||
function1: Expect.function(),
|
||||
function2: Expect.function(),
|
||||
});
|
||||
});
|
|
@ -51,3 +51,5 @@ skip-if = tsan # Times out on TSan, bug 1612707
|
|||
# See the comment on test_feature_stackwalking.js
|
||||
[test_merged_stacks.js]
|
||||
skip-if = (os == "mac" && release_or_beta) || (os == "linux" && release_or_beta && !debug) || asan || tsan
|
||||
|
||||
[test_assertion_helper.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче