Bug 1440782 Part 1 - Move PolicyEngine's JSON schema validator to toolkit r=Felipe

MozReview-Commit-ID: 41K9xzry21w

--HG--
rename : browser/components/enterprisepolicies/PoliciesValidator.jsm => toolkit/components/utils/JsonSchemaValidator.jsm
rename : toolkit/components/normandy/test/browser/.eslintrc.js => toolkit/components/utils/test/browser/.eslintrc.js
rename : browser/components/enterprisepolicies/tests/browser/browser_policies_validate_and_parse_API.js => toolkit/components/utils/test/browser/browser_JsonSchemaValidator.js
extra : rebase_source : ea227390d924cc14a8c5b0f8dde64efcc14bbcd1
This commit is contained in:
Mike Cooper 2018-04-17 16:36:05 -07:00
Родитель 7d19b65c4f
Коммит ab318eabb2
10 изменённых файлов: 422 добавлений и 341 удалений

Просмотреть файл

@ -9,7 +9,7 @@ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
WindowsGPOParser: "resource:///modules/policies/WindowsGPOParser.jsm",
Policies: "resource:///modules/policies/Policies.jsm",
PoliciesValidator: "resource:///modules/policies/PoliciesValidator.jsm",
JsonSchemaValidator: "resource://gre/modules/components-utils/JsonSchemaValidator.jsm",
});
// This is the file that will be searched for in the
@ -127,8 +127,7 @@ EnterprisePoliciesManager.prototype = {
}
let [parametersAreValid, parsedParameters] =
PoliciesValidator.validateAndParseParameters(policyParameters,
policySchema);
JsonSchemaValidator.validateAndParseParameters(policyParameters, policySchema);
if (!parametersAreValid) {
log.error(`Invalid parameters specified for ${policyName}.`);

Просмотреть файл

@ -24,7 +24,6 @@ EXTRA_COMPONENTS += [
EXTRA_JS_MODULES.policies += [
'Policies.jsm',
'PoliciesValidator.jsm',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':

Просмотреть файл

@ -18,7 +18,6 @@ support-files =
[browser_policies_setAndLockPref_API.js]
[browser_policies_simple_pref_policies.js]
[browser_policies_sorted_alphabetically.js]
[browser_policies_validate_and_parse_API.js]
[browser_policy_app_update.js]
[browser_policy_block_about_addons.js]
[browser_policy_block_about_config.js]

Просмотреть файл

@ -1,327 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* This file will test the parameters parsing and validation directly through
the PoliciesValidator API.
*/
const { PoliciesValidator } = ChromeUtils.import("resource:///modules/policies/PoliciesValidator.jsm", {});
add_task(async function test_boolean_values() {
let schema = {
type: "boolean"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters(true, schema);
ok(valid && parsed === true, "Parsed boolean value correctly");
[valid, parsed] = PoliciesValidator.validateAndParseParameters(false, schema);
ok(valid && parsed === false, "Parsed boolean value correctly");
[valid, parsed] = PoliciesValidator.validateAndParseParameters(0, schema);
ok(valid && parsed === false, "0 parsed as false correctly");
[valid, parsed] = PoliciesValidator.validateAndParseParameters(1, schema);
ok(valid && parsed === true, "1 parsed as true correctly");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters("0", schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters("true", schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters(2, schema)[0], "Other number values are not valid");
ok(!PoliciesValidator.validateAndParseParameters(undefined, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_number_values() {
let schema = {
type: "number"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters(1, schema);
ok(valid && parsed === 1, "Parsed number value correctly");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters("1", schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters(true, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_integer_values() {
// Integer is an alias for number
let schema = {
type: "integer"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters(1, schema);
ok(valid && parsed == 1, "Parsed integer value correctly");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters("1", schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters(true, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_string_values() {
let schema = {
type: "string"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters("foobar", schema);
ok(valid && parsed == "foobar", "Parsed string value correctly");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters(1, schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters(true, schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters(undefined, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_URL_values() {
let schema = {
type: "URL"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters("https://www.example.com/foo#bar", schema);
ok(valid, "URL is valid");
ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
is(parsed.prePath, "https://www.example.com", "prePath is correct");
is(parsed.pathQueryRef, "/foo#bar", "pathQueryRef is correct");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters("", schema)[0], "Empty string is not accepted for URL");
ok(!PoliciesValidator.validateAndParseParameters("www.example.com", schema)[0], "Scheme is required for URL");
ok(!PoliciesValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid URL");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
});
add_task(async function test_URLorEmpty_values() {
let schema = {
type: "URLorEmpty"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters("https://www.example.com/foo#bar", schema);
ok(valid, "URL is valid");
ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
is(parsed.prePath, "https://www.example.com", "prePath is correct");
is(parsed.pathQueryRef, "/foo#bar", "pathQueryRef is correct");
// Test that this type also accept empty strings
[valid, parsed] = PoliciesValidator.validateAndParseParameters("", schema);
ok(valid, "URLorEmpty is valid");
ok(!parsed, "parsed value is falsy");
is(typeof(parsed), "string", "parsed is a string");
is(parsed, "", "parsed is an empty string");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters(" ", schema)[0], "Non-empty string is not accepted");
ok(!PoliciesValidator.validateAndParseParameters("www.example.com", schema)[0], "Scheme is required for URL");
ok(!PoliciesValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid URL");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
});
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 valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters("https://www.example.com", schema);
ok(valid, "Origin is valid");
ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
is(parsed.prePath, "https://www.example.com", "prePath is correct");
is(parsed.pathQueryRef, "/", "pathQueryRef is corect");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters("https://www.example.com/foobar", schema)[0], "Origin cannot contain a path part");
ok(!PoliciesValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid origin");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
});
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"
}
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters([1, 2, 3], schema);
ok(valid, "Array is valid");
ok(Array.isArray(parsed), "parsed is an array");
is(parsed.length, 3, "array is correct");
// An empty array is also valid
[valid, parsed] = PoliciesValidator.validateAndParseParameters([], schema);
ok(valid, "Array is valid");
ok(Array.isArray(parsed), "parsed is an array");
is(parsed.length, 0, "array is correct");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters([1, true, 3], schema)[0], "Mixed types");
ok(!PoliciesValidator.validateAndParseParameters(2, schema)[0], "Type is correct but not in an array");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Object is not an array");
});
add_task(async function test_object_values() {
let schema = {
type: "object",
properties: {
url: {
type: "URL"
},
title: {
type: "string"
}
}
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters(
{
url: "https://www.example.com/foo#bar",
title: "Foo",
alias: "Bar"
},
schema);
ok(valid, "Object is valid");
ok(typeof(parsed) == "object", "parsed in an object");
ok(parsed.url instanceof Ci.nsIURI, "types inside the object are also parsed");
is(parsed.url.spec, "https://www.example.com/foo#bar", "URL was correctly parsed");
is(parsed.title, "Foo", "title was correctly parsed");
is(parsed.alias, undefined, "property not described in the schema is not present in the parsed object");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters(
{
url: "https://www.example.com/foo#bar",
title: 3,
},
schema)[0], "Mismatched type for title");
ok(!PoliciesValidator.validateAndParseParameters(
{
url: "www.example.com",
title: 3,
},
schema)[0], "Invalid URL inside the object");
});
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"
}
}
}
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters(
[{
url: "https://www.example.com/bookmark1",
title: "Foo",
},
{
url: "https://www.example.com/bookmark2",
title: "Bar",
}],
schema);
ok(valid, "Array is valid");
is(parsed.length, 2, "Correct number of items");
ok(typeof(parsed[0]) == "object" && typeof(parsed[1]) == "object", "Correct objects inside array");
is(parsed[0].url.spec, "https://www.example.com/bookmark1", "Correct URL for bookmark 1");
is(parsed[1].url.spec, "https://www.example.com/bookmark2", "Correct URL for bookmark 2");
is(parsed[0].title, "Foo", "Correct title for bookmark 1");
is(parsed[1].title, "Bar", "Correct title for bookmark 2");
});
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"
}
}
}
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters({
allow: [true, true, true]
}, schema);
ok(valid, "Object is valid");
is(parsed.allow.length, 3, "Allow array is correct.");
is(parsed.block, undefined, "Block array is undefined, as expected.");
});
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"]
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters({
"required-property": 5
}, schema);
ok(valid, "Object is valid since required property is present");
is(parsed["required-property"], 5, "required property is correct");
is(parsed["non-required-property"], undefined, "non-required property is undefined, as expected");
[valid, parsed] = PoliciesValidator.validateAndParseParameters({
"non-required-property": 5
}, schema);
ok(!valid, "Object is not valid since the required property is missing");
is(parsed, null, "Nothing was returned as parsed");
});

Просмотреть файл

@ -4,7 +4,7 @@
ChromeUtils.defineModuleGetter(this, "LogManager", "resource://normandy/lib/LogManager.jsm");
ChromeUtils.defineModuleGetter(this, "Uptake", "resource://normandy/lib/Uptake.jsm");
ChromeUtils.defineModuleGetter(this, "PoliciesValidator", "resource:///modules/policies/PoliciesValidator.jsm");
ChromeUtils.defineModuleGetter(this, "JsonSchemaValidator", "resource://gre/modules/components-utils/JsonSchemaValidator.jsm");
var EXPORTED_SYMBOLS = ["BaseAction"];
@ -72,7 +72,7 @@ class BaseAction {
return;
}
let [valid, validatedArguments] = PoliciesValidator.validateAndParseParameters(recipe.arguments, this.schema);
let [valid, validatedArguments] = JsonSchemaValidator.validateAndParseParameters(recipe.arguments, this.schema);
if (!valid) {
Cu.reportError(new Error(`Arguments do not match schema. arguments: ${JSON.stringify(recipe.arguments)}. schema: ${JSON.stringify(this.schema)}`));
Uptake.reportRecipe(recipe.id, Uptake.RECIPE_EXECUTION_ERROR);

Просмотреть файл

@ -2,27 +2,34 @@
* 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/. */
/* This file implements a not-quite standard JSON schema validator. It differs
* from the spec in a few ways:
*
* - the spec doesn't allow custom types to be defined, but this validator
* defines "URL", "URLorEmpty", "origin" etc.
* - Strings are automatically converted to nsIURIs for the appropriate types.
* - It doesn't support "pattern" when matching strings.
* - The boolean type accepts (and casts) 0 and 1 as valid values.
*/
"use strict";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
const PREF_LOGLEVEL = "browser.policies.loglevel";
XPCOMUtils.defineLazyGetter(this, "log", () => {
let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
return new ConsoleAPI({
prefix: "PoliciesValidator.jsm",
prefix: "JsonSchemaValidator.jsm",
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
// messages during development. See LOG_LEVELS in Console.jsm for details.
maxLogLevel: "error",
maxLogLevelPref: PREF_LOGLEVEL,
});
});
var EXPORTED_SYMBOLS = ["PoliciesValidator"];
var EXPORTED_SYMBOLS = ["JsonSchemaValidator"];
var PoliciesValidator = {
var JsonSchemaValidator = {
validateAndParseParameters(param, properties) {
return validateAndParseParamRecursive(param, properties);
}
@ -37,6 +44,24 @@ function validateAndParseParamRecursive(param, properties) {
}
log.debug(`checking @${param}@ for type ${properties.type}`);
if (Array.isArray(properties.type)) {
log.debug("type is an array");
// For an array of types, the value is valid if it matches any of the listed
// types. To check this, make versions of the object definition that include
// only one type at a time, and check the value against each one.
for (const type of properties.type) {
let typeProperties = Object.assign({}, properties, {type});
log.debug(`checking subtype ${type}`);
let [valid, data] = validateAndParseParamRecursive(param, typeProperties);
if (valid) {
return [true, data];
}
}
// None of the types matched
return [false, null];
}
switch (properties.type) {
case "boolean":
case "number":

Просмотреть файл

@ -13,5 +13,8 @@ EXTRA_COMPONENTS += [
]
EXTRA_JS_MODULES['components-utils'] = [
'ClientEnvironment.jsm'
'ClientEnvironment.jsm',
'JsonSchemaValidator.jsm',
]
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']

Просмотреть файл

@ -0,0 +1,11 @@
"use strict";
module.exports = {
extends: [
"plugin:mozilla/browser-test"
],
plugins: [
"mozilla"
],
};

Просмотреть файл

@ -0,0 +1 @@
[browser_JsonSchemaValidator.js]

Просмотреть файл

@ -0,0 +1,371 @@
/* 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"
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters(true, schema);
ok(valid && parsed === true, "Parsed boolean value correctly");
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters(false, schema);
ok(valid && parsed === false, "Parsed boolean value correctly");
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters(0, schema);
ok(valid && parsed === false, "0 parsed as false correctly");
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters(1, schema);
ok(valid && parsed === true, "1 parsed as true correctly");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters("0", schema)[0], "No type coercion");
ok(!JsonSchemaValidator.validateAndParseParameters("true", schema)[0], "No type coercion");
ok(!JsonSchemaValidator.validateAndParseParameters(2, schema)[0], "Other number values are not valid");
ok(!JsonSchemaValidator.validateAndParseParameters(undefined, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_number_values() {
let schema = {
type: "number"
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters(1, schema);
ok(valid && parsed === 1, "Parsed number value correctly");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters("1", schema)[0], "No type coercion");
ok(!JsonSchemaValidator.validateAndParseParameters(true, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_integer_values() {
// Integer is an alias for number
let schema = {
type: "integer"
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters(1, schema);
ok(valid && parsed == 1, "Parsed integer value correctly");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters("1", schema)[0], "No type coercion");
ok(!JsonSchemaValidator.validateAndParseParameters(true, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_string_values() {
let schema = {
type: "string"
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters("foobar", schema);
ok(valid && parsed == "foobar", "Parsed string value correctly");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters(1, schema)[0], "No type coercion");
ok(!JsonSchemaValidator.validateAndParseParameters(true, schema)[0], "No type coercion");
ok(!JsonSchemaValidator.validateAndParseParameters(undefined, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_URL_values() {
let schema = {
type: "URL"
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters("https://www.example.com/foo#bar", schema);
ok(valid, "URL is valid");
ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
is(parsed.prePath, "https://www.example.com", "prePath is correct");
is(parsed.pathQueryRef, "/foo#bar", "pathQueryRef is correct");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters("", schema)[0], "Empty string is not accepted for URL");
ok(!JsonSchemaValidator.validateAndParseParameters("www.example.com", schema)[0], "Scheme is required for URL");
ok(!JsonSchemaValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid URL");
ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
});
add_task(async function test_URLorEmpty_values() {
let schema = {
type: "URLorEmpty"
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters("https://www.example.com/foo#bar", schema);
ok(valid, "URL is valid");
ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
is(parsed.prePath, "https://www.example.com", "prePath is correct");
is(parsed.pathQueryRef, "/foo#bar", "pathQueryRef is correct");
// Test that this type also accept empty strings
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters("", schema);
ok(valid, "URLorEmpty is valid");
ok(!parsed, "parsed value is falsy");
is(typeof(parsed), "string", "parsed is a string");
is(parsed, "", "parsed is an empty string");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters(" ", schema)[0], "Non-empty string is not accepted");
ok(!JsonSchemaValidator.validateAndParseParameters("www.example.com", schema)[0], "Scheme is required for URL");
ok(!JsonSchemaValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid URL");
ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
});
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 valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters("https://www.example.com", schema);
ok(valid, "Origin is valid");
ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
is(parsed.prePath, "https://www.example.com", "prePath is correct");
is(parsed.pathQueryRef, "/", "pathQueryRef is corect");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters("https://www.example.com/foobar", schema)[0], "Origin cannot contain a path part");
ok(!JsonSchemaValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid origin");
ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
});
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"
}
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters([1, 2, 3], schema);
ok(valid, "Array is valid");
ok(Array.isArray(parsed), "parsed is an array");
is(parsed.length, 3, "array is correct");
// An empty array is also valid
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters([], schema);
ok(valid, "Array is valid");
ok(Array.isArray(parsed), "parsed is an array");
is(parsed.length, 0, "array is correct");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters([1, true, 3], schema)[0], "Mixed types");
ok(!JsonSchemaValidator.validateAndParseParameters(2, schema)[0], "Type is correct but not in an array");
ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Object is not an array");
});
add_task(async function test_object_values() {
let schema = {
type: "object",
properties: {
url: {
type: "URL"
},
title: {
type: "string"
}
}
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters(
{
url: "https://www.example.com/foo#bar",
title: "Foo",
alias: "Bar"
},
schema);
ok(valid, "Object is valid");
ok(typeof(parsed) == "object", "parsed in an object");
ok(parsed.url instanceof Ci.nsIURI, "types inside the object are also parsed");
is(parsed.url.spec, "https://www.example.com/foo#bar", "URL was correctly parsed");
is(parsed.title, "Foo", "title was correctly parsed");
is(parsed.alias, undefined, "property not described in the schema is not present in the parsed object");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters(
{
url: "https://www.example.com/foo#bar",
title: 3,
},
schema)[0], "Mismatched type for title");
ok(!JsonSchemaValidator.validateAndParseParameters(
{
url: "www.example.com",
title: 3,
},
schema)[0], "Invalid URL inside the object");
});
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"
}
}
}
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters(
[{
url: "https://www.example.com/bookmark1",
title: "Foo",
},
{
url: "https://www.example.com/bookmark2",
title: "Bar",
}],
schema);
ok(valid, "Array is valid");
is(parsed.length, 2, "Correct number of items");
ok(typeof(parsed[0]) == "object" && typeof(parsed[1]) == "object", "Correct objects inside array");
is(parsed[0].url.spec, "https://www.example.com/bookmark1", "Correct URL for bookmark 1");
is(parsed[1].url.spec, "https://www.example.com/bookmark2", "Correct URL for bookmark 2");
is(parsed[0].title, "Foo", "Correct title for bookmark 1");
is(parsed[1].title, "Bar", "Correct title for bookmark 2");
});
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"
}
}
}
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters({
allow: [true, true, true]
}, schema);
ok(valid, "Object is valid");
is(parsed.allow.length, 3, "Allow array is correct.");
is(parsed.block, undefined, "Block array is undefined, as expected.");
});
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"]
};
let valid, parsed;
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters({
"required-property": 5
}, schema);
ok(valid, "Object is valid since required property is present");
is(parsed["required-property"], 5, "required property is correct");
is(parsed["non-required-property"], undefined, "non-required property is undefined, as expected");
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters({
"non-required-property": 5
}, schema);
ok(!valid, "Object is not valid since the required property is missing");
is(parsed, null, "Nothing was returned as parsed");
});
add_task(async function test_number_or_string_values() {
let schema = {
type: ["number", "string"],
};
let valid, parsed;
// valid values
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters(1, schema);
ok(valid && parsed === 1, "Parsed number value correctly");
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters("foobar", schema);
ok(valid && parsed === "foobar", "Parsed string value correctly");
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters("1", schema);
ok(valid && parsed === "1", "Did not coerce string to number");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters(true, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_number_or_array_values() {
let schema = {
type: ["number", "array"],
items: {
type: "number",
}
};
let valid, parsed;
// valid values
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters(1, schema);
ok(valid, "Number is valid");
is(parsed, 1, "Parsed correctly");
ok(valid && parsed === 1, "Parsed number value correctly");
[valid, parsed] = JsonSchemaValidator.validateAndParseParameters([1, 2, 3], schema);
ok(valid, "Array is valid");
Assert.deepEqual(parsed, [1, 2, 3], "Parsed correctly");
// Invalid values:
ok(!JsonSchemaValidator.validateAndParseParameters(true, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters(["a", "b"], schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters([[]], schema)[0], "Invalid value");
ok(!JsonSchemaValidator.validateAndParseParameters([0, 1, [2, 3]], schema)[0], "Invalid value");
});