diff --git a/packages/builder/src/userInterface/pagesParsing/validatePages.js b/packages/builder/src/userInterface/pagesParsing/validatePages.js index 76429f5f8b..008420fe03 100644 --- a/packages/builder/src/userInterface/pagesParsing/validatePages.js +++ b/packages/builder/src/userInterface/pagesParsing/validatePages.js @@ -1,7 +1,6 @@ -import { recursivelyValidate } from "./validateProps"; -import { - isString, - keys, +import { + isString, + keys, flatten, isArray, map, @@ -15,12 +14,12 @@ export const validatePage = (page, getComponent) => { const error = message => errors.push(message); const noIndex = !page.index; - if(noIndex) { + if (noIndex) { error("Page does not define an index member"); } - if(!page.appBody - || !isString(page.appBody) + if (!page.appBody + || !isString(page.appBody) || !page.appBody.endsWith(".json")) { error("App body must be set toa valid JSON file"); } @@ -28,7 +27,7 @@ export const validatePage = (page, getComponent) => { /* Commenting this for now * index is a load of static members just now, but maybe useful for pageLayout props (which is just a pipe dream at time of writing) - const indexHtmlErrors = noIndex + const indexHtmlErrors = noIndex ? [] : pipe( recursivelyValidate(page.index, getComponent), [ @@ -44,17 +43,17 @@ export const validatePages = (pages, getComponent) => { let errors = []; const error = message => errors.push(message); - if(!pages.main) { + if (!pages.main) { error("must have a 'main' page"); } - if(!pages.unauthenticated) { + if (!pages.unauthenticated) { error("must have a 'unauthenticated' (login) page"); } - if(!pages.componentLibraries - || !isArray(pages.componentLibraries) - || pages.componentLibraries.length === 0) { + if (!pages.componentLibraries + || !isArray(pages.componentLibraries) + || pages.componentLibraries.length === 0) { error("componentLibraries must be set to a non-empty array of strings"); } @@ -67,4 +66,4 @@ export const validatePages = (pages, getComponent) => { ]); return [...errors, ...pageErrors]; -} \ No newline at end of file +} diff --git a/packages/builder/src/userInterface/pagesParsing/validateProps.js b/packages/builder/src/userInterface/pagesParsing/validateProps.js deleted file mode 100644 index 9f0f3ae11b..0000000000 --- a/packages/builder/src/userInterface/pagesParsing/validateProps.js +++ /dev/null @@ -1,143 +0,0 @@ -import { types } from "./types"; -import { - createProps, arrayElementComponentName -} from "./createProps"; -import { isString } from "util"; -import { - includes, filter, map, keys, - flatten, flattenDeep, each, - indexOf, isUndefined -} from "lodash/fp"; -import { common } from "../../../../core/src"; -import { - isBinding -} from "../../common/binding"; - -const pipe = common.$; - -const makeError = (errors, propName, stack) => (message) => - errors.push({ - stack, - propName, - error: message - }); - -export const recursivelyValidate = (rootProps, getComponent, stack = []) => { - - if (!rootProps._component) { - const errs = []; - makeError(errs, "_component", stack)("Component is not set"); - return errs; - // this would break everything else anyway - } - - const componentDef = getComponent( - rootProps._component); - - - const errors = validateProps( - componentDef, - rootProps, - stack, - true); - - const validateChildren = (_props, _stack) => - !_props._children - ? [] - : pipe(_props._children, [ - map(child => recursivelyValidate( - child, - getComponent, - [..._stack, _props._children.indexOf(child)])) - ]); - - const childErrors = validateChildren( - rootProps, stack); - - return flattenDeep([errors, ...childErrors]); -} - -const expandPropDef = propDef => - isString(propDef) - ? types[propDef].defaultDefinition() - : propDef; - - - -export const validateProps = (componentDefinition, props, stack = [], isFinal = true) => { - - const errors = []; - - if (isFinal && !props._component) { - makeError(errors, "_component", stack)("Component is not set"); - return errors; - // this would break everything else anyway - } - - const propsDefinition = componentDefinition.props; - - for (let propDefName in props) { - - const ignore = ['_component', "_children", '_layout', 'name', 'description', 'location']; - - if (ignore.includes(propDefName)) continue; - - const propDef = expandPropDef(propsDefinition[propDefName]); - - const type = types[propDef.type]; - - const error = makeError(errors, propDefName, stack); - - const propValue = props[propDefName]; - - // component declarations dont need to define al props. - if (!isFinal && isUndefined(propValue)) continue; - - if (isFinal && propDef.required && propValue) { - error(`Property ${propDefName} is required`); - continue; - } - - if (isBinding(propValue)) { - if (propDef.type === "event") { - error(`Cannot apply binding to type ${propDef.type}`); - continue; - } - } - else if (!type.isOfType(propValue)) { - error(`Property ${propDefName} is not of type ${propDef.type}. Actual value ${propValue}`) - continue; - } - - - if (propDef.type === "options" - && propValue - && !isBinding(propValue) - && !includes(propValue)(propDef.options)) { - error(`Property ${propDefName} is not one of allowed options. Acutal value is ${propValue}`); - } - - } - - return errors; -} - -export const validateComponentDefinition = (componentDefinition) => { - const { errors } = createProps(componentDefinition); - - const propDefinitions = expandPropDef(componentDefinition.props); - - pipe(propDefinitions, [ - keys, - map(k => ({ - propDef: propDefinitions[k], - propName: k - })), - filter(d => d.propDef.type === "options" - && (!d.propDef.options || d.propDef.options.length === 0)), - each(d => makeError(errors, d.propName)(`${d.propName} does not have any options`)) - ]); - - return errors; - -} diff --git a/packages/builder/tests/validateProps.spec.js b/packages/builder/tests/validateProps.spec.js deleted file mode 100644 index e2ab7dfda7..0000000000 --- a/packages/builder/tests/validateProps.spec.js +++ /dev/null @@ -1,232 +0,0 @@ -import { - validateComponentDefinition, - validateProps, - recursivelyValidate -} from "../src/userInterface/pagesParsing/validateProps"; -import { createProps } from "../src/userInterface/pagesParsing/createProps"; -import { - setBinding -} from "../src/common/binding"; - -// not that allot of this functionality is covered -// in createDefaultProps - as validate props uses that. - -describe("validateComponentDefinition", () => { - - - it("should return error when no options for options field", () => { - - const compDef = { - name:"some_component", - props: { - size: { - type: "options", - options: [] - } - } - }; - - const errors = validateComponentDefinition(compDef); - - expect(errors.length).toEqual(1); - expect(errors[0].propName).toBe("size"); - - }); - - it("should not return error when options field has options", () => { - - const compDef = { - name: "some_component", - props: { - size: { - type: "options", - options: ["small", "medium", "large"] - } - } - }; - - const errors = validateComponentDefinition(compDef); - - expect(errors).toEqual([]); - - }); - -}); - -const validComponentDef = { - name: "some_component", - props: { - size: { - type: "options", - options: ["small", "medium", "large"], - default:"medium" - }, - rowCount : "number" - } -}; - -const childComponentDef = { - name: "child_component", - props: { - width: "number", - units: { - type: "string", - default: "px" - } - } -}; - -const validProps = () => { - - const { props } = createProps(validComponentDef); - props._children.push( - createProps(childComponentDef)); - return props; -} - -describe("validateProps", () => { - - it("should have no errors with a big list of valid props", () => { - - const errors = validateProps(validComponentDef, validProps(), [], true); - expect(errors).toEqual([]); - - }); - - it("should return error with invalid value", () => { - - const props = validProps(); - props.rowCount = "1"; - const errors = validateProps(validComponentDef, props, [], true); - expect(errors.length).toEqual(1); - expect(errors[0].propName).toBe("rowCount"); - - }); - - it("should return error with invalid option", () => { - - const props = validProps(); - props.size = "really_small"; - const errors = validateProps(validComponentDef, props, [], true); - expect(errors.length).toEqual(1); - expect(errors[0].propName).toBe("size"); - - }); - - it("should not return error when has binding", () => { - const props = validProps(); - props._children[0].width = setBinding({path:"some_path"}); - props.size = setBinding({path:"other path", fallback:"small"}); - const errors = validateProps(validComponentDef, props, [], true); - expect(errors.length).toEqual(0); - }); -}); - -describe("recursivelyValidateProps", () => { - - const rootComponent = { - name: "rootComponent", - children: true, - props: { - width: "number" - } - }; - - const todoListComponent = { - name: "todoListComponent", - props:{ - showTitle: "bool" - } - }; - - const headerComponent = { - name: "headerComponent", - props: { - text: "string" - } - }; - - const iconComponent = { - name: "iconComponent", - props: { - iconName: "string" - } - }; - - const navItemComponent = { - name: "navItemComponent", - props: { - text: "string" - } - }; - - const getComponent = name => ({ - rootComponent, - todoListComponent, - headerComponent, - iconComponent, - navItemComponent - })[name]; - - const rootProps = () => ({ - _component: "rootComponent", - width: 100, - _children: [{ - _component: "todoListComponent", - showTitle: true, - _children : [ - { - _component: "navItemComponent", - text: "todos" - }, - { - _component: "headerComponent", - text: "Your todo list" - }, - { - _component: "iconComponent", - iconName: "fa fa-list" - }, - { - _component: "iconComponent", - iconName:"fa fa-cog" - } - ] - }] - }); - - it("should return no errors for valid structure", () => { - const result = recursivelyValidate( - rootProps(), - getComponent); - - expect(result).toEqual([]); - }); - - it("should return error on root component", () => { - const root = rootProps(); - root.width = "yeeeoooo"; - const result = recursivelyValidate(root, getComponent); - expect(result.length).toBe(1); - expect(result[0].propName).toBe("width"); - }); - - it("should return error on first nested child component", () => { - const root = rootProps(); - root._children[0].showTitle = "yeeeoooo"; - const result = recursivelyValidate(root, getComponent); - expect(result.length).toBe(1); - expect(result[0].stack).toEqual([0]); - expect(result[0].propName).toBe("showTitle"); - }); - - it("should return error on second nested child component", () => { - const root = rootProps(); - root._children[0]._children[0].text = false; - const result = recursivelyValidate(root, getComponent); - expect(result.length).toBe(1); - expect(result[0].stack).toEqual([0,0]); - expect(result[0].propName).toBe("text"); - }); - -}); \ No newline at end of file