summaryrefslogtreecommitdiffstats
path: root/toolkit/components/utils/test/browser
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /toolkit/components/utils/test/browser
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/utils/test/browser')
-rw-r--r--toolkit/components/utils/test/browser/browser.ini2
-rw-r--r--toolkit/components/utils/test/browser/browser_ClientEnvironment.js51
-rw-r--r--toolkit/components/utils/test/browser/browser_JsonSchemaValidator.js1964
3 files changed, 2017 insertions, 0 deletions
diff --git a/toolkit/components/utils/test/browser/browser.ini b/toolkit/components/utils/test/browser/browser.ini
new file mode 100644
index 0000000000..09d139f297
--- /dev/null
+++ b/toolkit/components/utils/test/browser/browser.ini
@@ -0,0 +1,2 @@
+[browser_ClientEnvironment.js]
+[browser_JsonSchemaValidator.js]
diff --git a/toolkit/components/utils/test/browser/browser_ClientEnvironment.js b/toolkit/components/utils/test/browser/browser_ClientEnvironment.js
new file mode 100644
index 0000000000..8ce0a6a228
--- /dev/null
+++ b/toolkit/components/utils/test/browser/browser_ClientEnvironment.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/*
+ * This can't be an xpcshell-test because, according to
+ * `toolkit/modules/Services.jsm`, "Not all applications implement
+ * nsIXULAppInfo (e.g. xpcshell doesn't)."
+ */
+const { ClientEnvironmentBase } = ChromeUtils.import(
+ "resource://gre/modules/components-utils/ClientEnvironment.jsm"
+);
+
+const { NormandyTestUtils } = ChromeUtils.import(
+ "resource://testing-common/NormandyTestUtils.jsm"
+);
+
+add_task(function testBuildId() {
+ ok(
+ ClientEnvironmentBase.appinfo !== undefined,
+ "appinfo should be available in the context"
+ );
+ ok(
+ typeof ClientEnvironmentBase.appinfo === "object",
+ "appinfo should be an object"
+ );
+ ok(
+ typeof ClientEnvironmentBase.appinfo.appBuildID === "string",
+ "buildId should be a string"
+ );
+});
+
+add_task(async function testRandomizationId() {
+ // Should generate an id if none is set
+ await SpecialPowers.clearUserPref("app.normandy.user_id");
+ ok(
+ NormandyTestUtils.isUuid(ClientEnvironmentBase.randomizationId),
+ "randomizationId should be available"
+ );
+
+ // Should read the right preference
+ await SpecialPowers.pushPrefEnv({
+ set: [["app.normandy.user_id", "fake id"]],
+ });
+ is(
+ ClientEnvironmentBase.randomizationId,
+ "fake id",
+ "randomizationId should read from preferences"
+ );
+});
diff --git a/toolkit/components/utils/test/browser/browser_JsonSchemaValidator.js b/toolkit/components/utils/test/browser/browser_JsonSchemaValidator.js
new file mode 100644
index 0000000000..47fb935fc6
--- /dev/null
+++ b/toolkit/components/utils/test/browser/browser_JsonSchemaValidator.js
@@ -0,0 +1,1964 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+ChromeUtils.import(
+ "resource://gre/modules/components-utils/JsonSchemaValidator.jsm",
+ this
+);
+
+add_task(async function test_boolean_values() {
+ let schema = {
+ type: "boolean",
+ };
+
+ // valid values
+ validate({
+ value: true,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: true,
+ },
+ });
+ validate({
+ value: false,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: false,
+ },
+ });
+ validate({
+ value: 0,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: false,
+ },
+ });
+ validate({
+ value: 1,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: true,
+ },
+ });
+
+ // Invalid values:
+ validate({
+ value: "0",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "0",
+ error: {
+ invalidValue: "0",
+ invalidPropertyNameComponents: [],
+ message: `The value '"0"' does not match the expected type 'boolean'`,
+ },
+ },
+ });
+ validate({
+ value: "true",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "true",
+ error: {
+ invalidValue: "true",
+ invalidPropertyNameComponents: [],
+ message: `The value '"true"' does not match the expected type 'boolean'`,
+ },
+ },
+ });
+ validate({
+ value: 2,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: 2,
+ error: {
+ invalidValue: 2,
+ invalidPropertyNameComponents: [],
+ message: `The value '2' does not match the expected type 'boolean'`,
+ },
+ },
+ });
+ validate({
+ value: undefined,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: undefined,
+ invalidPropertyNameComponents: [],
+ message: `The value 'undefined' does not match the expected type 'boolean'`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: {},
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match the expected type 'boolean'`,
+ },
+ },
+ });
+ validate({
+ value: null,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: null,
+ error: {
+ invalidValue: null,
+ invalidPropertyNameComponents: [],
+ message: `The value 'null' does not match the expected type 'boolean'`,
+ },
+ },
+ });
+});
+
+add_task(async function test_number_values() {
+ let schema = {
+ type: "number",
+ };
+
+ validate({
+ value: 1,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: 1,
+ },
+ });
+
+ // Invalid values:
+ validate({
+ value: "1",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "1",
+ error: {
+ invalidValue: "1",
+ invalidPropertyNameComponents: [],
+ message: `The value '"1"' does not match the expected type 'number'`,
+ },
+ },
+ });
+ validate({
+ value: true,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: true,
+ error: {
+ invalidValue: true,
+ invalidPropertyNameComponents: [],
+ message: `The value 'true' does not match the expected type 'number'`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: {},
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match the expected type 'number'`,
+ },
+ },
+ });
+ validate({
+ value: null,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: null,
+ error: {
+ invalidValue: null,
+ invalidPropertyNameComponents: [],
+ message: `The value 'null' does not match the expected type 'number'`,
+ },
+ },
+ });
+});
+
+add_task(async function test_integer_values() {
+ // Integer is an alias for number
+ let schema = {
+ type: "integer",
+ };
+
+ validate({
+ value: 1,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: 1,
+ },
+ });
+
+ // Invalid values:
+ validate({
+ value: "1",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "1",
+ error: {
+ invalidValue: "1",
+ invalidPropertyNameComponents: [],
+ message: `The value '"1"' does not match the expected type 'integer'`,
+ },
+ },
+ });
+ validate({
+ value: true,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: true,
+ error: {
+ invalidValue: true,
+ invalidPropertyNameComponents: [],
+ message: `The value 'true' does not match the expected type 'integer'`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: {},
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match the expected type 'integer'`,
+ },
+ },
+ });
+ validate({
+ value: null,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: null,
+ error: {
+ invalidValue: null,
+ invalidPropertyNameComponents: [],
+ message: `The value 'null' does not match the expected type 'integer'`,
+ },
+ },
+ });
+});
+
+add_task(async function test_null_values() {
+ let schema = {
+ type: "null",
+ };
+
+ validate({
+ value: null,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: null,
+ },
+ });
+
+ // Invalid values:
+ validate({
+ value: 1,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: 1,
+ error: {
+ invalidValue: 1,
+ invalidPropertyNameComponents: [],
+ message: `The value '1' does not match the expected type 'null'`,
+ },
+ },
+ });
+ validate({
+ value: "1",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "1",
+ error: {
+ invalidValue: "1",
+ invalidPropertyNameComponents: [],
+ message: `The value '"1"' does not match the expected type 'null'`,
+ },
+ },
+ });
+ validate({
+ value: true,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: true,
+ error: {
+ invalidValue: true,
+ invalidPropertyNameComponents: [],
+ message: `The value 'true' does not match the expected type 'null'`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: {},
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match the expected type 'null'`,
+ },
+ },
+ });
+ validate({
+ value: [],
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: [],
+ error: {
+ invalidValue: [],
+ invalidPropertyNameComponents: [],
+ message: `The value '[]' does not match the expected type 'null'`,
+ },
+ },
+ });
+});
+
+add_task(async function test_string_values() {
+ let schema = {
+ type: "string",
+ };
+
+ validate({
+ value: "foobar",
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: "foobar",
+ },
+ });
+
+ // Invalid values:
+ validate({
+ value: 1,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: 1,
+ error: {
+ invalidValue: 1,
+ invalidPropertyNameComponents: [],
+ message: `The value '1' does not match the expected type 'string'`,
+ },
+ },
+ });
+ validate({
+ value: true,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: true,
+ error: {
+ invalidValue: true,
+ invalidPropertyNameComponents: [],
+ message: `The value 'true' does not match the expected type 'string'`,
+ },
+ },
+ });
+ validate({
+ value: undefined,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: undefined,
+ invalidPropertyNameComponents: [],
+ message: `The value 'undefined' does not match the expected type 'string'`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: {},
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match the expected type 'string'`,
+ },
+ },
+ });
+ validate({
+ value: null,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: null,
+ error: {
+ invalidValue: null,
+ invalidPropertyNameComponents: [],
+ message: `The value 'null' does not match the expected type 'string'`,
+ },
+ },
+ });
+});
+
+add_task(async function test_URL_values() {
+ let schema = {
+ type: "URL",
+ };
+
+ let result = validate({
+ value: "https://www.example.com/foo#bar",
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: new URL("https://www.example.com/foo#bar"),
+ },
+ });
+ Assert.ok(result.parsedValue instanceof URL, "parsedValue is a URL");
+ Assert.equal(
+ result.parsedValue.origin,
+ "https://www.example.com",
+ "origin is correct"
+ );
+ Assert.equal(
+ result.parsedValue.pathname + result.parsedValue.hash,
+ "/foo#bar",
+ "pathname is correct"
+ );
+
+ // Invalid values:
+ validate({
+ value: "",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "",
+ error: {
+ invalidValue: "",
+ invalidPropertyNameComponents: [],
+ message: `The value '""' does not match the expected type 'URL'`,
+ },
+ },
+ });
+ validate({
+ value: "www.example.com",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "www.example.com",
+ error: {
+ invalidValue: "www.example.com",
+ invalidPropertyNameComponents: [],
+ message:
+ `The value '"www.example.com"' does not match the expected ` +
+ `type 'URL'`,
+ },
+ },
+ });
+ validate({
+ value: "https://:!$%",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "https://:!$%",
+ error: {
+ invalidValue: "https://:!$%",
+ invalidPropertyNameComponents: [],
+ message: `The value '"https://:!$%"' does not match the expected type 'URL'`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: {},
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match the expected type 'URL'`,
+ },
+ },
+ });
+});
+
+add_task(async function test_URLorEmpty_values() {
+ let schema = {
+ type: "URLorEmpty",
+ };
+
+ let result = validate({
+ value: "https://www.example.com/foo#bar",
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: new URL("https://www.example.com/foo#bar"),
+ },
+ });
+ Assert.ok(result.parsedValue instanceof URL, "parsedValue is a URL");
+ Assert.equal(
+ result.parsedValue.origin,
+ "https://www.example.com",
+ "origin is correct"
+ );
+ Assert.equal(
+ result.parsedValue.pathname + result.parsedValue.hash,
+ "/foo#bar",
+ "pathname is correct"
+ );
+
+ // Test that this type also accept empty strings
+ result = validate({
+ value: "",
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: "",
+ },
+ });
+ Assert.equal(typeof result.parsedValue, "string", "parsedValue is a string");
+
+ // Invalid values:
+ validate({
+ value: " ",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: " ",
+ error: {
+ invalidValue: " ",
+ invalidPropertyNameComponents: [],
+ message: `The value '" "' does not match the expected type 'URLorEmpty'`,
+ },
+ },
+ });
+ validate({
+ value: "www.example.com",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "www.example.com",
+ error: {
+ invalidValue: "www.example.com",
+ invalidPropertyNameComponents: [],
+ message:
+ `The value '"www.example.com"' does not match the expected ` +
+ `type 'URLorEmpty'`,
+ },
+ },
+ });
+ validate({
+ value: "https://:!$%",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "https://:!$%",
+ error: {
+ invalidValue: "https://:!$%",
+ invalidPropertyNameComponents: [],
+ message:
+ `The value '"https://:!$%"' does not match the expected ` +
+ `type 'URLorEmpty'`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: {},
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match the expected type 'URLorEmpty'`,
+ },
+ },
+ });
+});
+
+add_task(async function test_origin_values() {
+ // Origin is a URL that doesn't contain a path/query string (i.e., it's only scheme + host + port)
+ let schema = {
+ type: "origin",
+ };
+
+ let result = validate({
+ value: "https://www.example.com",
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: new URL("https://www.example.com/"),
+ },
+ });
+ Assert.ok(result.parsedValue instanceof URL, "parsedValue is a URL");
+ Assert.equal(
+ result.parsedValue.origin,
+ "https://www.example.com",
+ "origin is correct"
+ );
+ Assert.equal(
+ result.parsedValue.pathname + result.parsedValue.hash,
+ "/",
+ "pathname is correct"
+ );
+
+ // Invalid values:
+ validate({
+ value: "https://www.example.com/foobar",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: new URL("https://www.example.com/foobar"),
+ error: {
+ invalidValue: "https://www.example.com/foobar",
+ invalidPropertyNameComponents: [],
+ message:
+ `The value '"https://www.example.com/foobar"' does not match the ` +
+ `expected type 'origin'`,
+ },
+ },
+ });
+ validate({
+ value: "https://:!$%",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "https://:!$%",
+ error: {
+ invalidValue: "https://:!$%",
+ invalidPropertyNameComponents: [],
+ message:
+ `The value '"https://:!$%"' does not match the expected ` +
+ `type 'origin'`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: {},
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match the expected type 'origin'`,
+ },
+ },
+ });
+});
+
+add_task(async function test_origin_file_values() {
+ // File URLs can also be origins
+ let schema = {
+ type: "origin",
+ };
+
+ let result = validate({
+ value: "file:///foo/bar",
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: new URL("file:///foo/bar"),
+ },
+ });
+ Assert.ok(result.parsedValue instanceof URL, "parsedValue is a URL");
+ Assert.equal(
+ result.parsedValue.href,
+ "file:///foo/bar",
+ "Should get what we passed in"
+ );
+});
+
+add_task(async function test_origin_file_values() {
+ // File URLs can also be origins
+ let schema = {
+ type: "origin",
+ };
+
+ let result = validate({
+ value: "file:///foo/bar/foobar.html",
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: new URL("file:///foo/bar/foobar.html"),
+ },
+ });
+ Assert.ok(result.parsedValue instanceof URL, "parsedValue is a URL");
+ Assert.equal(
+ result.parsedValue.href,
+ "file:///foo/bar/foobar.html",
+ "Should get what we passed in"
+ );
+});
+
+add_task(async function test_array_values() {
+ // The types inside an array object must all be the same
+ let schema = {
+ type: "array",
+ items: {
+ type: "number",
+ },
+ };
+
+ validate({
+ value: [1, 2, 3],
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: [1, 2, 3],
+ },
+ });
+
+ // An empty array is also valid
+ validate({
+ value: [],
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: [],
+ },
+ });
+
+ // Invalid values:
+ validate({
+ value: [1, true, 3],
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: true,
+ error: {
+ invalidValue: true,
+ invalidPropertyNameComponents: [1],
+ message:
+ `The value 'true' does not match the expected type 'number'. The ` +
+ `invalid value is property '1' in [1,true,3]`,
+ },
+ },
+ });
+ validate({
+ value: 2,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: 2,
+ invalidPropertyNameComponents: [],
+ message: `The value '2' does not match the expected type 'array'`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match the expected type 'array'`,
+ },
+ },
+ });
+});
+
+add_task(async function test_non_strict_arrays() {
+ // Non-strict arrays ignores invalid values (don't include
+ // them in the parsed output), instead of failing the validation.
+ // Note: invalid values might still report errors to the console.
+ let schema = {
+ type: "array",
+ strict: false,
+ items: {
+ type: "string",
+ },
+ };
+
+ validate({
+ value: ["valid1", "valid2", false, 3, "valid3"],
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: ["valid1", "valid2", "valid3"],
+ },
+ });
+
+ // Checks that strict defaults to true;
+ delete schema.strict;
+ validate({
+ value: ["valid1", "valid2", false, 3, "valid3"],
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: false,
+ error: {
+ invalidValue: false,
+ invalidPropertyNameComponents: [2],
+ message:
+ `The value 'false' does not match the expected type 'string'. The ` +
+ `invalid value is property '2' in ` +
+ `["valid1","valid2",false,3,"valid3"]`,
+ },
+ },
+ });
+
+ // Pass allowArrayNonMatchingItems, should be valid
+ validate({
+ value: ["valid1", "valid2", false, 3, "valid3"],
+ schema,
+ options: {
+ allowArrayNonMatchingItems: true,
+ },
+ expectedResult: {
+ valid: true,
+ parsedValue: ["valid1", "valid2", "valid3"],
+ },
+ });
+});
+
+add_task(async function test_object_values() {
+ // valid values below
+
+ validate({
+ value: {
+ foo: "hello",
+ bar: 123,
+ },
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "string",
+ },
+ bar: {
+ type: "number",
+ },
+ },
+ },
+ expectedResult: {
+ valid: true,
+ parsedValue: {
+ foo: "hello",
+ bar: 123,
+ },
+ },
+ });
+
+ validate({
+ value: {
+ foo: "hello",
+ bar: {
+ baz: 123,
+ },
+ },
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "string",
+ },
+ bar: {
+ type: "object",
+ properties: {
+ baz: {
+ type: "number",
+ },
+ },
+ },
+ },
+ },
+ expectedResult: {
+ valid: true,
+ parsedValue: {
+ foo: "hello",
+ bar: {
+ baz: 123,
+ },
+ },
+ },
+ });
+
+ // allowExtraProperties
+ let result = validate({
+ value: {
+ url: "https://www.example.com/foo#bar",
+ title: "Foo",
+ alias: "Bar",
+ },
+ schema: {
+ type: "object",
+ properties: {
+ url: {
+ type: "URL",
+ },
+ title: {
+ type: "string",
+ },
+ },
+ },
+ options: {
+ allowExtraProperties: true,
+ },
+ expectedResult: {
+ valid: true,
+ parsedValue: {
+ url: new URL("https://www.example.com/foo#bar"),
+ title: "Foo",
+ },
+ },
+ });
+ Assert.ok(
+ result.parsedValue.url instanceof URL,
+ "types inside the object are also parsed"
+ );
+ Assert.equal(
+ result.parsedValue.url.href,
+ "https://www.example.com/foo#bar",
+ "URL was correctly parsed"
+ );
+
+ // allowExplicitUndefinedProperties
+ validate({
+ value: {
+ foo: undefined,
+ },
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "string",
+ },
+ },
+ },
+ options: {
+ allowExplicitUndefinedProperties: true,
+ },
+ expectedResult: {
+ valid: true,
+ parsedValue: {},
+ },
+ });
+
+ // allowNullAsUndefinedProperties
+ validate({
+ value: {
+ foo: null,
+ },
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "string",
+ },
+ },
+ },
+ options: {
+ allowNullAsUndefinedProperties: true,
+ },
+ expectedResult: {
+ valid: true,
+ parsedValue: {},
+ },
+ });
+
+ // invalid values below
+
+ validate({
+ value: null,
+ schema: {
+ type: "object",
+ },
+ expectedResult: {
+ valid: false,
+ parsedValue: null,
+ error: {
+ invalidValue: null,
+ invalidPropertyNameComponents: [],
+ message: `The value 'null' does not match the expected type 'object'`,
+ },
+ },
+ });
+
+ validate({
+ value: {
+ url: "not a URL",
+ },
+ schema: {
+ type: "object",
+ properties: {
+ url: {
+ type: "URL",
+ },
+ },
+ },
+ expectedResult: {
+ valid: false,
+ parsedValue: "not a URL",
+ error: {
+ invalidValue: "not a URL",
+ invalidPropertyNameComponents: ["url"],
+ message:
+ `The value '"not a URL"' does not match the expected type 'URL'. ` +
+ `The invalid value is property 'url' in {"url":"not a URL"}`,
+ },
+ },
+ });
+
+ validate({
+ value: "test",
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "string",
+ },
+ },
+ },
+ expectedResult: {
+ valid: false,
+ error: {
+ invalidValue: "test",
+ invalidPropertyNameComponents: [],
+ message: `The value '"test"' does not match the expected type 'object'`,
+ },
+ },
+ });
+
+ validate({
+ value: {
+ foo: 123,
+ },
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "string",
+ },
+ },
+ },
+ expectedResult: {
+ valid: false,
+ parsedValue: 123,
+ error: {
+ invalidValue: 123,
+ invalidPropertyNameComponents: ["foo"],
+ message:
+ `The value '123' does not match the expected type 'string'. ` +
+ `The invalid value is property 'foo' in {"foo":123}`,
+ },
+ },
+ });
+
+ validate({
+ value: {
+ foo: {
+ bar: 456,
+ },
+ },
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "object",
+ properties: {
+ bar: {
+ type: "string",
+ },
+ },
+ },
+ },
+ },
+ expectedResult: {
+ valid: false,
+ parsedValue: 456,
+ error: {
+ invalidValue: 456,
+ invalidPropertyNameComponents: ["foo", "bar"],
+ message:
+ `The value '456' does not match the expected type 'string'. ` +
+ `The invalid value is property 'foo.bar' in {"foo":{"bar":456}}`,
+ },
+ },
+ });
+
+ // null non-required property with strict=true: invalid
+ validate({
+ value: {
+ foo: null,
+ },
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "string",
+ },
+ },
+ },
+ expectedResult: {
+ valid: false,
+ parsedValue: null,
+ error: {
+ invalidValue: null,
+ invalidPropertyNameComponents: ["foo"],
+ message:
+ `The value 'null' does not match the expected type 'string'. ` +
+ `The invalid value is property 'foo' in {"foo":null}`,
+ },
+ },
+ });
+ validate({
+ value: {
+ foo: null,
+ },
+ schema: {
+ type: "object",
+ strict: true,
+ properties: {
+ foo: {
+ type: "string",
+ },
+ },
+ },
+ options: {
+ allowNullAsUndefinedProperties: true,
+ },
+ expectedResult: {
+ valid: false,
+ parsedValue: null,
+ error: {
+ invalidValue: null,
+ invalidPropertyNameComponents: ["foo"],
+ message:
+ `The value 'null' does not match the expected type 'string'. ` +
+ `The invalid value is property 'foo' in {"foo":null}`,
+ },
+ },
+ });
+
+ // non-null falsey non-required property with strict=false: invalid
+ validate({
+ value: {
+ foo: false,
+ },
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "string",
+ },
+ },
+ },
+ options: {
+ allowExplicitUndefinedProperties: true,
+ allowNullAsUndefinedProperties: true,
+ },
+ expectedResult: {
+ valid: false,
+ parsedValue: false,
+ error: {
+ invalidValue: false,
+ invalidPropertyNameComponents: ["foo"],
+ message:
+ `The value 'false' does not match the expected type 'string'. ` +
+ `The invalid value is property 'foo' in {"foo":false}`,
+ },
+ },
+ });
+ validate({
+ value: {
+ foo: false,
+ },
+ schema: {
+ type: "object",
+ strict: false,
+ properties: {
+ foo: {
+ type: "string",
+ },
+ },
+ },
+ expectedResult: {
+ valid: false,
+ parsedValue: false,
+ error: {
+ invalidValue: false,
+ invalidPropertyNameComponents: ["foo"],
+ message:
+ `The value 'false' does not match the expected type 'string'. ` +
+ `The invalid value is property 'foo' in {"foo":false}`,
+ },
+ },
+ });
+
+ validate({
+ value: {
+ bogus: "test",
+ },
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "string",
+ },
+ },
+ },
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: { bogus: "test" },
+ invalidPropertyNameComponents: [],
+ message: `Object has unexpected property 'bogus'`,
+ },
+ },
+ });
+
+ validate({
+ value: {
+ foo: {
+ bogus: "test",
+ },
+ },
+ schema: {
+ type: "object",
+ properties: {
+ foo: {
+ type: "object",
+ properties: {
+ bar: {
+ type: "string",
+ },
+ },
+ },
+ },
+ },
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: { bogus: "test" },
+ invalidPropertyNameComponents: ["foo"],
+ message:
+ `Object has unexpected property 'bogus'. The invalid value is ` +
+ `property 'foo' in {"foo":{"bogus":"test"}}`,
+ },
+ },
+ });
+});
+
+add_task(async function test_array_of_objects() {
+ // This schema is used, for example, for bookmarks
+ let schema = {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ url: {
+ type: "URL",
+ },
+ title: {
+ type: "string",
+ },
+ },
+ },
+ };
+
+ validate({
+ value: [
+ {
+ url: "https://www.example.com/bookmark1",
+ title: "Foo",
+ },
+ {
+ url: "https://www.example.com/bookmark2",
+ title: "Bar",
+ },
+ ],
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: [
+ {
+ url: new URL("https://www.example.com/bookmark1"),
+ title: "Foo",
+ },
+ {
+ url: new URL("https://www.example.com/bookmark2"),
+ title: "Bar",
+ },
+ ],
+ },
+ });
+});
+
+add_task(async function test_missing_arrays_inside_objects() {
+ let schema = {
+ type: "object",
+ properties: {
+ allow: {
+ type: "array",
+ items: {
+ type: "boolean",
+ },
+ },
+ block: {
+ type: "array",
+ items: {
+ type: "boolean",
+ },
+ },
+ },
+ };
+
+ validate({
+ value: {
+ allow: [true, true, true],
+ },
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: {
+ allow: [true, true, true],
+ },
+ },
+ });
+});
+
+add_task(async function test_required_vs_nonrequired_properties() {
+ let schema = {
+ type: "object",
+ properties: {
+ "non-required-property": {
+ type: "number",
+ },
+
+ "required-property": {
+ type: "number",
+ },
+ },
+ required: ["required-property"],
+ };
+
+ validate({
+ value: {
+ "required-property": 5,
+ "non-required-property": undefined,
+ },
+ schema,
+ options: {
+ allowExplicitUndefinedProperties: true,
+ },
+ expectedResult: {
+ valid: true,
+ parsedValue: {
+ "required-property": 5,
+ },
+ },
+ });
+
+ validate({
+ value: {
+ "non-required-property": 5,
+ },
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: {
+ "non-required-property": 5,
+ },
+ invalidPropertyNameComponents: [],
+ message: `Object is missing required property 'required-property'`,
+ },
+ },
+ });
+});
+
+add_task(async function test_number_or_string_values() {
+ let schema = {
+ type: ["number", "string"],
+ };
+
+ validate({
+ value: 1,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: 1,
+ },
+ });
+ validate({
+ value: "foobar",
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: "foobar",
+ },
+ });
+ validate({
+ value: "1",
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: "1",
+ },
+ });
+
+ // Invalid values:
+ validate({
+ value: true,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: true,
+ invalidPropertyNameComponents: [],
+ message: `The value 'true' does not match any type in ["number","string"]`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match any type in ["number","string"]`,
+ },
+ },
+ });
+ validate({
+ value: null,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: null,
+ invalidPropertyNameComponents: [],
+ message: `The value 'null' does not match any type in ["number","string"]`,
+ },
+ },
+ });
+});
+
+add_task(async function test_number_or_array_values() {
+ let schema = {
+ type: ["number", "array"],
+ items: {
+ type: "number",
+ },
+ };
+
+ validate({
+ value: 1,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: 1,
+ },
+ });
+ validate({
+ value: [1, 2, 3],
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: [1, 2, 3],
+ },
+ });
+
+ // Invalid values:
+ validate({
+ value: true,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: true,
+ invalidPropertyNameComponents: [],
+ message: `The value 'true' does not match any type in ["number","array"]`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match any type in ["number","array"]`,
+ },
+ },
+ });
+ validate({
+ value: null,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: null,
+ invalidPropertyNameComponents: [],
+ message: `The value 'null' does not match any type in ["number","array"]`,
+ },
+ },
+ });
+ validate({
+ value: ["a", "b"],
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: ["a", "b"],
+ invalidPropertyNameComponents: [],
+ message: `The value '["a","b"]' does not match any type in ["number","array"]`,
+ },
+ },
+ });
+ validate({
+ value: [[]],
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: [[]],
+ invalidPropertyNameComponents: [],
+ message: `The value '[[]]' does not match any type in ["number","array"]`,
+ },
+ },
+ });
+ validate({
+ value: [0, 1, [2, 3]],
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: [0, 1, [2, 3]],
+ invalidPropertyNameComponents: [],
+ message:
+ `The value '[0,1,[2,3]]' does not match any type in ` +
+ `["number","array"]`,
+ },
+ },
+ });
+});
+
+add_task(function test_number_or_null_Values() {
+ let schema = {
+ type: ["number", "null"],
+ };
+
+ validate({
+ value: 1,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: 1,
+ },
+ });
+ validate({
+ value: null,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: null,
+ },
+ });
+
+ // Invalid values:
+ validate({
+ value: true,
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: true,
+ invalidPropertyNameComponents: [],
+ message: `The value 'true' does not match any type in ["number","null"]`,
+ },
+ },
+ });
+ validate({
+ value: "string",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: "string",
+ invalidPropertyNameComponents: [],
+ message: `The value '"string"' does not match any type in ["number","null"]`,
+ },
+ },
+ });
+ validate({
+ value: {},
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: {},
+ invalidPropertyNameComponents: [],
+ message: `The value '{}' does not match any type in ["number","null"]`,
+ },
+ },
+ });
+ validate({
+ value: ["a", "b"],
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: ["a", "b"],
+ invalidPropertyNameComponents: [],
+ message: `The value '["a","b"]' does not match any type in ["number","null"]`,
+ },
+ },
+ });
+});
+
+add_task(async function test_patternProperties() {
+ let schema = {
+ type: "object",
+ properties: {
+ "S-bool-property": { type: "boolean" },
+ },
+ patternProperties: {
+ "^S-": { type: "string" },
+ "^N-": { type: "number" },
+ "^B-": { type: "boolean" },
+ },
+ };
+
+ validate({
+ value: {
+ "S-string": "test",
+ "N-number": 5,
+ "B-boolean": true,
+ "S-bool-property": false,
+ },
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: {
+ "S-string": "test",
+ "N-number": 5,
+ "B-boolean": true,
+ "S-bool-property": false,
+ },
+ },
+ });
+
+ validate({
+ value: {
+ "N-string": "test",
+ },
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: "test",
+ error: {
+ invalidValue: "test",
+ invalidPropertyNameComponents: ["N-string"],
+ message:
+ `The value '"test"' does not match the expected type 'number'. ` +
+ `The invalid value is property 'N-string' in {"N-string":"test"}`,
+ },
+ },
+ });
+
+ validate({
+ value: {
+ "S-number": 5,
+ },
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: 5,
+ error: {
+ invalidValue: 5,
+ invalidPropertyNameComponents: ["S-number"],
+ message:
+ `The value '5' does not match the expected type 'string'. ` +
+ `The invalid value is property 'S-number' in {"S-number":5}`,
+ },
+ },
+ });
+
+ schema = {
+ type: "object",
+ patternProperties: {
+ "[": { " type": "string" },
+ },
+ };
+
+ Assert.throws(
+ () => JsonSchemaValidator.validate({}, schema),
+ /Invalid property pattern/,
+ "Checking that invalid property patterns throw"
+ );
+});
+
+add_task(async function test_JSON_type() {
+ let schema = {
+ type: "JSON",
+ };
+
+ validate({
+ value: {
+ a: "b",
+ },
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: {
+ a: "b",
+ },
+ },
+ });
+ validate({
+ value: '{"a": "b"}',
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: {
+ a: "b",
+ },
+ },
+ });
+
+ validate({
+ value: "{This{is{not{JSON}}}}",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: "{This{is{not{JSON}}}}",
+ invalidPropertyNameComponents: [],
+ message: `JSON string could not be parsed: "{This{is{not{JSON}}}}"`,
+ },
+ },
+ });
+ validate({
+ value: "0",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: "0",
+ invalidPropertyNameComponents: [],
+ message: `JSON was not an object: "0"`,
+ },
+ },
+ });
+ validate({
+ value: "true",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: "true",
+ invalidPropertyNameComponents: [],
+ message: `JSON was not an object: "true"`,
+ },
+ },
+ });
+});
+
+add_task(async function test_enum() {
+ let schema = {
+ type: "string",
+ enum: ["one", "two"],
+ };
+
+ validate({
+ value: "one",
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: "one",
+ },
+ });
+
+ validate({
+ value: "three",
+ schema,
+ expectedResult: {
+ valid: false,
+ parsedValue: undefined,
+ error: {
+ invalidValue: "three",
+ invalidPropertyNameComponents: [],
+ message:
+ `The value '"three"' is not one of the enumerated values ` +
+ `["one","two"]`,
+ },
+ },
+ });
+});
+
+add_task(async function test_bool_enum() {
+ let schema = {
+ type: "boolean",
+ enum: ["one", "two"],
+ };
+
+ // `enum` is ignored because `type` is boolean.
+ validate({
+ value: true,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: true,
+ },
+ });
+});
+
+add_task(async function test_boolint_enum() {
+ let schema = {
+ type: "boolean",
+ enum: ["one", "two"],
+ };
+
+ // `enum` is ignored because `type` is boolean and the integer value was
+ // coerced to boolean.
+ validate({
+ value: 1,
+ schema,
+ expectedResult: {
+ valid: true,
+ parsedValue: true,
+ },
+ });
+});
+
+/**
+ * Validates a value against a schema and asserts that the result is as
+ * expected.
+ *
+ * @param {*} value
+ * The value to validate.
+ * @param {object} schema
+ * The schema to validate against.
+ * @param {object} expectedResult
+ * The expected result. See JsonSchemaValidator.validate for what this object
+ * should look like. If the expected result is invalid, then this object
+ * should have an `error` property with all the properties of validation
+ * errors, including `message`, except that `rootValue` and `rootSchema` are
+ * unnecessary because this function will add them for you.
+ * @param {object} options
+ * Options to pass to JsonSchemaValidator.validate.
+ * @return {object} The return value of JsonSchemaValidator.validate, which is
+ * a result.
+ */
+function validate({ value, schema, expectedResult, options = undefined }) {
+ let result = JsonSchemaValidator.validate(value, schema, options);
+
+ checkObject(
+ result,
+ expectedResult,
+ {
+ valid: false,
+ parsedValue: true,
+ },
+ "Checking result property: "
+ );
+
+ Assert.equal("error" in result, "error" in expectedResult, "result.error");
+ if (result.error && expectedResult.error) {
+ expectedResult.error = Object.assign(expectedResult.error, {
+ rootValue: value,
+ rootSchema: schema,
+ });
+ checkObject(
+ result.error,
+ expectedResult.error,
+ {
+ rootValue: true,
+ rootSchema: false,
+ invalidPropertyNameComponents: false,
+ invalidValue: true,
+ message: false,
+ },
+ "Checking result.error property: "
+ );
+ }
+
+ return result;
+}
+
+/**
+ * Asserts that an object is the same as an expected object.
+ *
+ * @param {*} actual
+ * The actual object.
+ * @param {*} expected
+ * The expected object.
+ * @param {object} properties
+ * The properties to compare in the two objects. This value should be an
+ * object. The keys are the names of properties in the two objects. The
+ * values are booleans: true means that the property should be compared using
+ * strict equality and false means deep equality. Deep equality is used if
+ * the property is an object.
+ */
+function checkObject(actual, expected, properties, message) {
+ for (let [name, strict] of Object.entries(properties)) {
+ let assertFunc =
+ !strict || typeof expected[name] == "object"
+ ? "deepEqual"
+ : "strictEqual";
+ Assert[assertFunc](actual[name], expected[name], message + name);
+ }
+}