| @@ -14,6 +14,7 @@ | |||
| }, | |||
| "homepage": "https://github.com/VCVRack/community#readme", | |||
| "devDependencies": { | |||
| "ajv": "^5.5.2", | |||
| "jasmine": "^2.8.0", | |||
| "node-virustotal": "^2.4.2", | |||
| "request": "^2.83.0" | |||
| @@ -0,0 +1,27 @@ | |||
| # Jasmine Tests | |||
| [Jasmine Intro](https://jasmine.github.io/2.8/introduction.html) | |||
| To run all tests: | |||
| ``` | |||
| npm test | |||
| ``` | |||
| To run only the zip tests for example: | |||
| ``` | |||
| ./node_modules/.bin/jasmine --filter=zip | |||
| ``` | |||
| # Virus Total | |||
| **Rate Limit: 4 per minute** | |||
| `VT_API_KEY` env var is required and already set in travisci settings | |||
| [Public API](https://www.virustotal.com/en/documentation/public-api/v2/) | |||
| [Node Module](https://github.com/natewatson999/node-virustotal) | |||
| @@ -0,0 +1,59 @@ | |||
| const CUR_VER_PREFIX = '0.5'; | |||
| const fs = require("fs"); | |||
| const Ajv = require('ajv'); //https://github.com/epoberezkin/ajv | |||
| const ajv = new Ajv({}); | |||
| const validate = ajv.compile(require('./manifest.json')); | |||
| describe("json", function() { | |||
| it("manifest files should be valid against the schema", function(done) { | |||
| fs.readdir('plugins', function(err, files) { | |||
| if (err){ | |||
| fail("unable to read plugins dir"); | |||
| } | |||
| for (let index in files) { | |||
| const fileName = files[index]; | |||
| const filePath = `plugins/${fileName}`; | |||
| if(!filePath.toLowerCase().endsWith('.json')){ | |||
| fail("manifests should have .json extension"); | |||
| } | |||
| fs.readFile(filePath, 'utf8', (err, fileContent) => { | |||
| if (err){ | |||
| fail("unable to read manifest file"); | |||
| } | |||
| let manifestObj; | |||
| try { | |||
| const obj = JSON.parse(fileContent); | |||
| const valid = validate(obj); | |||
| if (!valid) { | |||
| validate.errors.map(e=>e.message+=` in ${filePath}`) | |||
| fail(validate.errors); | |||
| } | |||
| if(!(/^[a-zA-Z0-9_\-]*$/).test(obj.slug)){ | |||
| fail(`slug does not match regex in ${filePath}`); | |||
| } | |||
| if(fileName.replace('.json','') !== obj.slug){ | |||
| fail(`slug '${obj.slug}' does not match fileName: ${fileName}`); | |||
| } | |||
| if(obj.version && !obj.version.startsWith(CUR_VER_PREFIX)){ | |||
| fail(`version '${obj.version}' must start with '${CUR_VER_PREFIX}'`); | |||
| } | |||
| done(); | |||
| } catch(err){ | |||
| fail(`Invalid JSON: ${filePath}\n${err}`); | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| }); | |||
| @@ -1,35 +0,0 @@ | |||
| //Jasmine Test - https://jasmine.github.io/2.8/introduction.html | |||
| const fs = require("fs"); | |||
| describe("json", function() { | |||
| it("manifest files should be valid parsable json", function(done) { | |||
| fs.readdir('plugins', function(err, files) { | |||
| if (err){ | |||
| fail("unable to read plugins dir"); | |||
| } | |||
| for (let index in files) { | |||
| const filePath = `plugins/${files[index]}`; | |||
| if(!filePath.toLowerCase().endsWith('.json')){ | |||
| fail("manifests should have .json extension"); | |||
| } | |||
| fs.readFile(filePath, 'utf8', (err, fileContent) => { | |||
| if (err){ | |||
| fail("unable to read manifest file"); | |||
| } | |||
| let manifestObj; | |||
| try { | |||
| manifestObj = JSON.parse(fileContent); | |||
| done(); | |||
| } catch(err){ | |||
| fail(`Invalid JSON: ${filePath}\n${err}`); | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,100 @@ | |||
| { | |||
| "id": "manifest.json", | |||
| "type": "object", | |||
| "required": [ | |||
| "slug", | |||
| "version" | |||
| ], | |||
| "description": "A plugin manifest file for VCV Rack.", | |||
| "properties": { | |||
| "slug": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "name": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "author": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "license": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "version": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "homepage": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "donation": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "manual": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "source": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "disabled": { | |||
| "type": "boolean", | |||
| "description": "used when a plugin needs to be disabled" | |||
| }, | |||
| "productId": { | |||
| "type": "number", | |||
| "description": "only used by vcv rack website" | |||
| }, | |||
| "downloads": { | |||
| "type": "object", | |||
| "properties": { | |||
| "win": { | |||
| "type": "object", | |||
| "properties": { | |||
| "download": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "sha256": { | |||
| "type": "string", | |||
| "description": "" | |||
| } | |||
| } | |||
| }, | |||
| "lin": { | |||
| "type": "object", | |||
| "properties": { | |||
| "download": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "sha256": { | |||
| "type": "string", | |||
| "description": "" | |||
| } | |||
| } | |||
| }, | |||
| "mac": { | |||
| "type": "object", | |||
| "properties": { | |||
| "download": { | |||
| "type": "string", | |||
| "description": "" | |||
| }, | |||
| "sha256": { | |||
| "type": "string", | |||
| "description": "" | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| }, | |||
| "additionalProperties": false | |||
| } | |||