diff --git a/platform/test/resources/TestActivityFiles.js b/platform/test/resources/TestActivityFiles.js index 92f6c38..5b3ebcc 100644 --- a/platform/test/resources/TestActivityFiles.js +++ b/platform/test/resources/TestActivityFiles.js @@ -22,7 +22,17 @@ export const ACTIVITY_2PANELS_1ACTION = `{ "id": "panel-2", "name": "Panel 2", "ref": "paneldef-t1", - "file": "file2.ext" + "file": "file2.ext", + "buttons": [ + { + "id": "panel-button-1", + "icon": "icon", + "actionfunction": "function-1" + }, + { + "ref": "action-button" + } + ] } ], "actions": [ diff --git a/platform/test/resources/TestUtility.js b/platform/test/resources/TestUtility.js index eae094e..e20fd82 100644 --- a/platform/test/resources/TestUtility.js +++ b/platform/test/resources/TestUtility.js @@ -15,7 +15,14 @@ export function configObjectEquals(configA, configB){ return (strConfigA === strConfigB) } +// Jasmine custom matchers export const customMatchers = { + + /** + * Matcher to check the expected keywords are present in the give input. + * where actual is a Sting and expected is an array of strings that contains + * the keyword to check. + */ toContainKeywords : function(matchersUtil){ return { compare: function(actual, expected){ @@ -28,7 +35,7 @@ export const customMatchers = { result.pass = containedResults.every(r => r); if (result.pass){ - result.message= "test"; // Negated not case + result.message= `Expected the input to contain none of the keywords '${expected.toString()}' however at least one was found. input: '${actual}'`; // Negated not case } else { result.message= `Expected the input to contain all of the keywords '${expected.toString()}' however at least one was missing. input: '${actual}'`; } diff --git a/platform/test/spec/testActivityConfigValidatorSpec.js b/platform/test/spec/testActivityConfigValidatorSpec.js index fa4085e..ab9636b 100644 --- a/platform/test/spec/testActivityConfigValidatorSpec.js +++ b/platform/test/spec/testActivityConfigValidatorSpec.js @@ -1,8 +1,7 @@ -/*global describe, it, expect, beforeEach -- functions provided by Jasmine */ +/*global describe, it, expect, beforeEach, jasmine -- functions provided by Jasmine */ import { ActivityConfigValidator } from "../../src/ActivityConfigValidator.js" import { ACTIVITY_2PANELS_1ACTION } from "../resources/TestActivityFiles.js"; -import { ConfigValidationError } from "../../src/ConfigValidationError.js" - +import {customMatchers, checkErrorPopulated} from "../resources/TestUtility.js" const EXPECTED_FILE_TYPE = "ActivityConfig"; @@ -18,12 +17,13 @@ describe("ActivityConfigValidator", () => { }) }) - describe("validate tool configuration", () => { + describe("validate activity configuration", () => { let acv; let activityConfig; //Setup beforeEach( () => { + jasmine.addMatchers(customMatchers); activityConfig = JSON.parse(ACTIVITY_2PANELS_1ACTION); acv = new ActivityConfigValidator(); }) @@ -36,7 +36,10 @@ describe("ActivityConfigValidator", () => { expect(errors).toHaveSize(0); }) - it("returns errors that are ConfigValidationError instances",() => { + /*-------------------------------------------------------- + * Activity + *--------------------------------------------------------*/ + it("returns an error if the config has no id key", () => { delete activityConfig.activities[0].id; // Call the target object @@ -44,12 +47,60 @@ describe("ActivityConfigValidator", () => { let e = errors[0]; // Check the expected results - expect(e).toBeInstanceOf(ConfigValidationError); - expect(e.fileType).toEqual(EXPECTED_FILE_TYPE); + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "id"], "/activities/0"); }) - it("returns an error if the config has no id key", () => { - delete activityConfig.activities[0].id; + it("returns an error if the config has no title key", () => { + delete activityConfig.activities[0].title; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "title"], "/activities/0"); + }) + + it("returns an error if the config has no tools key", () => { + delete activityConfig.activities[0].tools; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "tools"], "/activities/0"); + }) + + it("returns an error if the config has no layout key", () => { + delete activityConfig.activities[0].layout; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "layout"], "/activities/0"); + }) + + it("returns an error if the config has no actions key", () => { + delete activityConfig.activities[0].actions; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "actions"], "/activities/0"); + }) + + it("returns an error if the config has no panels key", () => { + delete activityConfig.activities[0].panels; // Call the target object let errors = acv.validateConfigFile(activityConfig); @@ -57,9 +108,78 @@ describe("ActivityConfigValidator", () => { // Check the expected results expect(errors).toHaveSize(1); - checkErrorPopulated(e); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "panels"], "/activities/0"); }) + /*-------------------------------------------------------- + * Layout + *--------------------------------------------------------*/ + it("returns an error if the config layout has no area key", () => { + delete activityConfig.activities[0].layout.area; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "area"], "/activities/0/layout"); + }) + + /*-------------------------------------------------------- + * Actions + *--------------------------------------------------------*/ + it("returns an error if a config action has no source key", () => { + delete activityConfig.activities[0].actions[0].source; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "source"], "/activities/0/actions/0"); + }) + + it("returns an error if a config action has no sourceButton key", () => { + delete activityConfig.activities[0].actions[0].sourceButton; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "sourceButton"], "/activities/0/actions/0"); + }) + + it("returns an error if a config action has no parameters key", () => { + delete activityConfig.activities[0].actions[0].parameters; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "parameters"], "/activities/0/actions/0"); + }) + + it("returns an error if a config action has no output key", () => { + delete activityConfig.activities[0].actions[0].output; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "output"], "/activities/0/actions/0"); + }) + + /*-------------------------------------------------------- + * Panels + *--------------------------------------------------------*/ it("returns an error if a panel in config has no id key", () => { delete activityConfig.activities[0].panels[1].id; @@ -69,16 +189,74 @@ describe("ActivityConfigValidator", () => { // Check the expected results expect(errors).toHaveSize(1); - checkErrorPopulated(e); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "id"], "/activities/0/panels/1"); + }) + + it("returns an error if a panel in config has no name key", () => { + delete activityConfig.activities[0].panels[1].name; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "name"], "/activities/0/panels/1"); + }) + + it("returns an error if a panel in config has no ref key", () => { + delete activityConfig.activities[0].panels[1].ref; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(1); + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "ref"], "/activities/0/panels/1"); + }) + + /* ---- Buttons ---- */ + it("returns an error if a panel button in config has no id key", () => { + delete activityConfig.activities[0].panels[1].buttons[0].id; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(3); // As Json schema oneOf is used to allow either ref or a button so a ref error + // and oneOf error are also returned + + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "id"], "/activities/0/panels/1/buttons/0"); + }) + + it("returns an error if a panel button in config has no icon key", () => { + delete activityConfig.activities[0].panels[1].buttons[0].icon; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[0]; + + // Check the expected results + expect(errors).toHaveSize(3); // As Json schema oneOf is used to allow either ref or a button so a ref + // and oneOf errors are also returned + + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "icon"], "/activities/0/panels/1/buttons/0"); + }) + + it("returns an error if a panel button ref in config has no ref key", () => { + delete activityConfig.activities[0].panels[1].buttons[1].ref; + + // Call the target object + let errors = acv.validateConfigFile(activityConfig); + let e = errors[2]; + + // Check the expected results + expect(errors).toHaveSize(4); // As Json schema oneOf is used to allow either ref or a button so a button + // and oneOf errors are also returned + + checkErrorPopulated(e, "required", EXPECTED_FILE_TYPE, ["required", "ref"], "/activities/0/panels/1/buttons/1"); }) }) }) - -function checkErrorPopulated(error){ - const MIN_LENGTH = 3; - expect(error).toBeInstanceOf(ConfigValidationError); - expect(error.fileType).toEqual(EXPECTED_FILE_TYPE); - expect(error.location.length).toBeGreaterThanOrEqual(MIN_LENGTH); - expect(error.category.length).toBeGreaterThanOrEqual(MIN_LENGTH); - expect(error.message.length).toBeGreaterThanOrEqual(MIN_LENGTH); -} \ No newline at end of file