diff --git a/api-metadata.schema.json b/api-metadata.schema.json new file mode 100644 index 00000000..affd3435 --- /dev/null +++ b/api-metadata.schema.json @@ -0,0 +1,45 @@ +{ + "definitions": { + "function_metadata": { + "type": "object", + "properties": { + "minArgs": { + "description": "The minimum number of arguments which must be passed to the function. If called with fewer than this number of arguments, the wrapper will raise an exception.", + "type": "number" + }, + "maxArgs": { + "description": "The maximum number of arguments which may be passed to the function. If called with more than this number of arguments, the wrapper will raise an exception.", + "type": "number" + }, + "fallbackToNoCallback": { + "description": "If the function doesn't take a callback parameter in some browsers and throws an error when a callback is passed.", + "type": "boolean" + }, + "singleCallbackArg": { + "description": "If the function callback takes only one parameter.\nSet to `false` to enforce that the function's varargs are never unwrapped.", + "type": "boolean" + } + }, + "required": ["minArgs", "maxArgs"], + "additionalProperties": false + }, + + "namespace_metadata": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^(?!(maxArgs|minArgs|noCallback)$).*": { + "anyOf": [ + { "$ref": "#/definitions/function_metadata" }, + { "$ref": "#/definitions/namespace_metadata" } + ] + } + } + } + }, + + "additionalProperties": { + "description": "Top-level namespace", + "$ref": "#/definitions/namespace_metadata" + } +} diff --git a/package.json b/package.json index 1e88d0c7..64da61dc 100644 --- a/package.json +++ b/package.json @@ -17,12 +17,14 @@ }, "homepage": "https://github.com/mozilla/webextension-polyfill", "devDependencies": { + "ajv": "^6.7.0", "async-wait-until": "^1.1.5", "babel-core": "^6.26.3", "babel-eslint": "^8.0.1", "babel-plugin-transform-es2015-modules-umd": "^6.24.1", "babel-preset-babili": "^0.0.10", "babel-preset-es2017": "^6.24.1", + "better-ajv-errors": "^0.6.1", "browserify": "^16.2.2", "chai": "^3.5.0", "chromedriver": "^2.38.3", diff --git a/test/test-schema.js b/test/test-schema.js new file mode 100644 index 00000000..36292ca0 --- /dev/null +++ b/test/test-schema.js @@ -0,0 +1,37 @@ +"use strict"; + +const {assert} = require("chai"); +const Ajv = require("ajv"); +const betterAjvErrors = (() => { + // Wrapper to work around https://github.com/atlassian/better-ajv-errors/pull/21 + const _betterAjvErrors = require("better-ajv-errors"); + function betterAjvErrors(schema, data, errors, options) { + return errors + .map(e => _betterAjvErrors(schema, data, [e], options)) + .join("\n\n"); + } + + Object.setPrototypeOf(betterAjvErrors, _betterAjvErrors); + + return /** @type {typeof _betterAjvErrors} */ (betterAjvErrors); +})(); + +const ajv = new Ajv({jsonPointers: true, allErrors: true}); + +const schema = require("../api-metadata.schema.json"); +const data = require("../api-metadata.json"); + +describe("api-metadata.json", () => { + it("api-metadata.json matches schema", () => { + const valid = ajv.validate(schema, data); + if (!valid) { + assert.fail( + undefined, + undefined, + `API Metadata doesn't match schema: + +${betterAjvErrors(schema, data, ajv.errors, {indent: 2})}`, + ); + } + }); +});