Skip to content

Commit

Permalink
[IMP] validation: add support for value types
Browse files Browse the repository at this point in the history
This commit add supports for value types in prop validation. To describe
a value V type, one has to simply write {value: V}.

Note that this commit also improves the validation typing.

closes odoo#1198
closes odoo#910
  • Loading branch information
ged-odoo authored and sdegueldre committed Jun 15, 2022
1 parent 55ac43c commit 9c4c3e3
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 4 deletions.
2 changes: 2 additions & 0 deletions doc/reference/props.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ For each key, a `prop` definition is either a boolean, a constructor, a list of
- a boolean: indicate that the props exists, and is mandatory.
- a constructor: this should describe the type, for example: `id: Number` describe
the props `id` as a number
- an object describing a value as type. This is done by using the `value` key. For example, `{value: false}` specifies that the corresponding value should be equal to false.
- a list of constructors. In that case, this means that we allow more than one
type. For example, `id: [Number, String]` means that `id` can be either a string
or a number.
Expand Down Expand Up @@ -240,6 +241,7 @@ class ComponentB extends owl.Component {
size: {
validate: e => ["small", "medium", "large"].includes(e)
},
someId: [Number, {value: false}], // either a number or false
};
```
Expand Down
14 changes: 10 additions & 4 deletions src/runtime/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ type BaseType =
| true
| "*";

type UnionType = (TypeInfo | BaseType)[];
type TypeDescription = BaseType | UnionType | TypeInfo;

interface TypeInfo {
type?: TypeDescription;
optional?: boolean;
Expand All @@ -19,15 +16,20 @@ interface TypeInfo {
element?: TypeDescription;
}

type ValueType = { value: any };

type TypeDescription = BaseType | TypeInfo | ValueType | TypeDescription[];
type SimplifiedSchema = string[];
type NormalizedSchema = { [key: string]: TypeDescription };
export type Schema = SimplifiedSchema | NormalizedSchema;

// -----------------------------------------------------------------------------
// helpers
// -----------------------------------------------------------------------------
const isUnionType = (t: TypeDescription): t is UnionType => Array.isArray(t);
const isUnionType = (t: TypeDescription): t is TypeDescription[] => Array.isArray(t);
const isBaseType = (t: TypeDescription): t is BaseType => typeof t !== "object";
const isValueType = (t: TypeDescription): t is ValueType =>
typeof t === "object" && t && "value" in t;

export function isOptional(t: TypeDescription): Boolean {
return typeof t === "object" && "optional" in t ? t.optional || false : false;
Expand All @@ -42,6 +44,8 @@ function describe(info: TypeDescription): string {
return describeType(info);
} else if (isUnionType(info)) {
return info.map(describe).join(" or ");
} else if (isValueType(info)) {
return String(info.value);
}
if ("element" in info) {
return `list of ${describe({ type: info.element, optional: false })}s`;
Expand Down Expand Up @@ -134,6 +138,8 @@ function validateType(key: string, value: any, descr: TypeDescription): string |
return isOptional(descr) ? null : `'${key}' is undefined (should be a ${describe(descr)})`;
} else if (isBaseType(descr)) {
return validateBaseType(key, value, descr);
} else if (isValueType(descr)) {
return value === descr.value ? null : `'${key}' is not equal to '${descr.value}'`;
} else if (isUnionType(descr)) {
let validDescr = descr.find((p) => !validateType(key, value, p));
return validDescr ? null : `'${key}' is not a ${describe(descr)}`;
Expand Down
26 changes: 26 additions & 0 deletions tests/validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,4 +248,30 @@ describe("validateSchema", () => {
expect(validateSchema({ size: "sall" }, schema)).toEqual(["'size' is not valid"]);
expect(validateSchema({ size: 1 }, schema)).toEqual(["'size' is not a string"]);
});

test("value as type", () => {
expect(validateSchema({ a: false }, { a: { value: false } })).toEqual([]);
expect(validateSchema({ a: true }, { a: { value: false } })).toEqual([
"'a' is not equal to 'false'",
]);
});

test("value as type (some other values)", () => {
expect(validateSchema({ a: null }, { a: { value: null } })).toEqual([]);
expect(validateSchema({ a: false }, { a: { value: null } })).toEqual([
"'a' is not equal to 'null'",
]);
expect(validateSchema({ a: "hey" }, { a: { value: "hey" } })).toEqual([]);
expect(validateSchema({ a: true }, { a: { value: "hey" } })).toEqual([
"'a' is not equal to 'hey'",
]);
});

test("value as type work in union type", () => {
expect(validateSchema({ a: false }, { a: [String, { value: false }] })).toEqual([]);
expect(validateSchema({ a: true }, { a: [String, { value: false }] })).toEqual([
"'a' is not a string or false",
]);
expect(validateSchema({ a: "string" }, { a: [String, { value: false }] })).toEqual([]);
});
});

0 comments on commit 9c4c3e3

Please sign in to comment.