// import { Component, Env } from "../../src/component/component";
// import { makeTestFixture, makeTestEnv, nextTick } from "../helpers";
// import { useState } from "../../src/hooks";
// import { QWeb } from "../../src/qweb";
// import { xml } from "../../src/tags";
// //------------------------------------------------------------------------------
// // Setup and helpers
// //------------------------------------------------------------------------------
// let fixture: HTMLElement;
// let env: Env;
// let dev: boolean = false;
// beforeEach(() => {
// fixture = makeTestFixture();
// env = makeTestEnv();
// Component.env = env;
// dev = QWeb.dev;
// QWeb.dev = true;
// });
// afterEach(() => {
// fixture.remove();
// QWeb.dev = dev;
// });
// class Widget extends Component {}
//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------
describe.skip("props validation", () => {
test("validation is only done in dev mode", async () => {
// class TestWidget extends Widget {
// static props = ["message"];
// static template = xml`
hey
`;
// }
// class Parent extends Widget {
// static components = { TestWidget };
// static template = xml`
`;
// }
// let error;
// QWeb.dev = true;
// try {
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe(`Missing props 'message' (component 'TestWidget')`);
// error = undefined;
// QWeb.dev = false;
// try {
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
});
test("props: list of strings", async () => {
// class TestWidget extends Widget {
// static props = ["message"];
// static template = xml`hey
`;
// }
// class Parent extends Widget {
// static components = { TestWidget };
// static template = xml`
`;
// }
// let error;
// try {
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe(`Missing props 'message' (component 'TestWidget')`);
});
test("validate simple types", async () => {
// const Tests = [
// { type: Number, ok: 1, ko: "1" },
// { type: Boolean, ok: true, ko: "1" },
// { type: String, ok: "1", ko: 1 },
// { type: Object, ok: {}, ko: "1" },
// { type: Date, ok: new Date(), ko: "1" },
// { type: Function, ok: () => {}, ko: "1" },
// ];
// let props;
// class Parent extends Component {
// static template = xml`
`;
// get p() {
// return props.p;
// }
// }
// for (let test of Tests) {
// let TestWidget = class extends Widget {
// static template = xml`hey
`;
// static props = { p: test.type };
// };
// Parent.components = { TestWidget };
// let error;
// props = {};
// try {
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe(`Missing props 'p' (component '_a')`);
// error = undefined;
// props = { p: test.ok };
// try {
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// props = { p: test.ko };
// try {
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'p' in component '_a'");
// }
});
test("validate simple types, alternate form", async () => {
// const Tests = [
// { type: Number, ok: 1, ko: "1" },
// { type: Boolean, ok: true, ko: "1" },
// { type: String, ok: "1", ko: 1 },
// { type: Object, ok: {}, ko: "1" },
// { type: Date, ok: new Date(), ko: "1" },
// { type: Function, ok: () => {}, ko: "1" },
// ];
// let props;
// class Parent extends Component {
// static template = xml`
`;
// get p() {
// return props.p;
// }
// }
// for (let test of Tests) {
// let TestWidget = class extends Component {
// static props = { p: { type: test.type } };
// static template = xml`hey
`;
// };
// Parent.components = { TestWidget };
// let error;
// props = {};
// try {
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe(`Missing props 'p' (component '_a')`);
// error = undefined;
// props = { p: test.ok };
// try {
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// props = { p: test.ko };
// try {
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'p' in component '_a'");
// }
});
test("can validate a prop with multiple types", async () => {
// class TestWidget extends Component {
// static template = xml`hey
`;
// static props = { p: [String, Boolean] };
// }
// class Parent extends Component {
// static template = xml`
`;
// static components = { TestWidget };
// get p() {
// return props.p;
// }
// }
// let error;
// let props;
// try {
// props = { p: "string" };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: true };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: 1 };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
});
test("can validate an optional props", async () => {
// class TestWidget extends Component {
// static template = xml`hey
`;
// static props = { p: { type: String, optional: true } };
// }
// class Parent extends Component {
// static template = xml`
`;
// static components = { TestWidget };
// get p() {
// return props.p;
// }
// }
// let error;
// let props;
// try {
// props = { p: "key" };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = {};
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: 1 };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
});
test("can validate an array with given primitive type", async () => {
// class TestWidget extends Component {
// static template = xml`hey
`;
// static props = { p: { type: Array, element: String } };
// }
// class Parent extends Component {
// static template = xml`
`;
// static components = { TestWidget };
// get p() {
// return props.p;
// }
// }
// let error;
// let props;
// try {
// props = { p: [] };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: ["string"] };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: [1] };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// error = undefined;
// try {
// props = { p: ["string", 1] };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
});
test("can validate an array with multiple sub element types", async () => {
// class TestWidget extends Component {
// static template = xml`hey
`;
// static props = { p: { type: Array, element: [String, Boolean] } };
// }
// class Parent extends Component {
// static template = xml`
`;
// static components = { TestWidget };
// get p() {
// return props.p;
// }
// }
// let error;
// let props;
// try {
// props = { p: [] };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: ["string"] };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: [false, true, "string"] };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: [true, 1] };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
});
test("can validate an object with simple shape", async () => {
// class TestWidget extends Component {
// static template = xml`hey
`;
// static props = {
// p: { type: Object, shape: { id: Number, url: String } },
// };
// }
// class Parent extends Component {
// static template = xml`
`;
// static components = { TestWidget };
// get p() {
// return props.p;
// }
// }
// let error;
// let props;
// try {
// props = { p: { id: 1, url: "url" } };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: { id: 1, url: "url", extra: true } };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid prop 'p' in component TestWidget (unknown prop 'extra')");
// try {
// props = { p: { id: "1", url: "url" } };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
// error = undefined;
// try {
// props = { p: { id: 1 } };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
});
test("can validate recursively complicated prop def", async () => {
// class TestWidget extends Component {
// static template = xml`hey
`;
// static props = {
// p: {
// type: Object,
// shape: {
// id: Number,
// url: [Boolean, { type: Array, element: Number }],
// },
// },
// };
// }
// class Parent extends Component {
// static template = xml`
`;
// static components = { TestWidget };
// get p() {
// return props.p;
// }
// }
// let error;
// let props;
// try {
// props = { p: { id: 1, url: true } };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: { id: 1, url: [12] } };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// props = { p: { id: 1, url: [12, true] } };
// const p = new Parent();
// await p.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
});
test("can validate optional attributes in nested sub props", () => {
// class TestComponent extends Component {
// static props = {
// myprop: {
// type: Array,
// element: {
// type: Object,
// shape: {
// num: { type: Number, optional: true },
// },
// },
// },
// };
// }
// let error;
// try {
// QWeb.utils.validateProps(TestComponent, { myprop: [{}] });
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// QWeb.utils.validateProps(TestComponent, { myprop: [{ a: 1 }] });
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe(
// "Invalid prop 'myprop' in component TestComponent (unknown prop 'a')"
// );
});
test("can validate with a custom validator", () => {
// class TestComponent extends Component {
// static props = {
// size: {
// validate: (e) => ["small", "medium", "large"].includes(e),
// },
// };
// }
// let error;
// try {
// QWeb.utils.validateProps(TestComponent, { size: "small" });
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// try {
// QWeb.utils.validateProps(TestComponent, { size: "abcdef" });
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'size' in component 'TestComponent'");
});
test("can validate with a custom validator, and a type", () => {
// const validator = jest.fn((n) => 0 <= n && n <= 10);
// class TestComponent extends Component {
// static props = {
// n: {
// type: Number,
// validate: validator,
// },
// };
// }
// let error;
// try {
// QWeb.utils.validateProps(TestComponent, { n: 3 });
// } catch (e) {
// error = e;
// }
// expect(error).toBeUndefined();
// expect(validator).toBeCalledTimes(1);
// try {
// QWeb.utils.validateProps(TestComponent, { n: "str" });
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'n' in component 'TestComponent'");
// expect(validator).toBeCalledTimes(1);
// error = null;
// try {
// QWeb.utils.validateProps(TestComponent, { n: 100 });
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Invalid Prop 'n' in component 'TestComponent'");
// expect(validator).toBeCalledTimes(2);
});
test("props are validated in dev mode (code snapshot)", async () => {
// env.qweb.addTemplates(`
//
//
//
//
//
// `);
// class Child extends Widget {
// static props = ["message"];
// }
// class App extends Widget {
// static components = { Child };
// }
// const app = new App();
// await app.mount(fixture);
// expect(fixture.innerHTML).toBe("");
// // need to make sure there are 2 call to update props. one at component
// // creation, and one at update time.
// expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
});
test("props: list of strings with optional props", async () => {
// class TestWidget extends Widget {
// static props = ["message", "someProp?"];
// }
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { someProp: 1 });
// }).toThrow();
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { message: 1 });
// }).not.toThrow();
});
test("props: can be defined with a boolean", async () => {
// class TestWidget extends Widget {
// static props = { message: true };
// }
// expect(() => {
// QWeb.utils.validateProps(TestWidget, {});
// }).toThrow();
});
test("props with type array, and no element", async () => {
// class TestWidget extends Widget {
// static props = { myprop: { type: Array } };
// }
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { myprop: [1] });
// }).not.toThrow();
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { myprop: 1 });
// }).toThrow(`Invalid Prop 'myprop' in component 'TestWidget'`);
});
test("props with type object, and no shape", async () => {
// class TestWidget extends Widget {
// static props = { myprop: { type: Object } };
// }
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { myprop: { a: 3 } });
// }).not.toThrow();
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { myprop: false });
// }).toThrow(`Invalid Prop 'myprop' in component 'TestWidget'`);
});
test("props: extra props cause an error", async () => {
// class TestWidget extends Widget {
// static props = ["message"];
// }
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { message: 1, flag: true });
// }).toThrow();
});
test("props: extra props cause an error, part 2", async () => {
// class TestWidget extends Widget {
// static props = { message: true };
// }
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { message: 1, flag: true });
// }).toThrow();
});
test("props: optional prop do not cause an error", async () => {
// class TestWidget extends Widget {
// static props = ["message?"];
// }
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { message: 1 });
// }).not.toThrow();
});
test("optional prop do not cause an error if value is undefined", async () => {
// class TestWidget extends Widget {
// static props = { message: { type: String, optional: true } };
// }
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { message: undefined });
// }).not.toThrow();
// expect(() => {
// QWeb.utils.validateProps(TestWidget, { message: null });
// }).toThrow();
});
test("missing required boolean prop causes an error", async () => {
// class TestWidget extends Widget {
// static props = ["p"];
// static template = xml`hey`;
// }
// class App extends Widget {
// static template = xml`
`;
// static components = { TestWidget };
// }
// const w = new App(undefined, {});
// let error;
// try {
// await w.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Missing props 'p' (component 'TestWidget')");
});
test("props are validated whenever component is updated", async () => {
// let error;
// class TestWidget extends Component {
// static props = { p: { type: Number } };
// static template = xml`
`;
// async __updateProps() {
// try {
// await Component.prototype.__updateProps.apply(this, arguments);
// } catch (e) {
// error = e;
// }
// }
// }
// class Parent extends Component {
// static template = xml`
`;
// static components = { TestWidget };
// state: any = useState({ p: 1 });
// }
// const w = new Parent();
// await w.mount(fixture);
// expect(fixture.innerHTML).toBe("");
// w.state.p = undefined;
// await nextTick();
// expect(error).toBeDefined();
// expect(error.message).toBe("Missing props 'p' (component 'TestWidget')");
});
test("default values are applied before validating props at update", async () => {
// class TestWidget extends Component {
// static props = { p: { type: Number } };
// static template = xml`
`;
// static defaultProps = { p: 4 };
// }
// class Parent extends Component {
// static template = xml`
`;
// static components = { TestWidget };
// state: any = useState({ p: 1 });
// }
// const w = new Parent();
// await w.mount(fixture);
// expect(fixture.innerHTML).toBe("");
// w.state.p = undefined;
// await nextTick();
// expect(fixture.innerHTML).toBe("");
});
test("mix of optional and mandatory", async () => {
// class Child extends Component {
// static props = {
// optional: { type: String, optional: true },
// mandatory: Number,
// };
// static template = xml`
`;
// }
// class App extends Component {
// static components = { Child };
// static template = xml`
`;
// }
// const w = new App(undefined, {});
// let error;
// try {
// await w.mount(fixture);
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// expect(error.message).toBe("Missing props 'mandatory' (component 'Child')");
});
});
describe.skip("default props", () => {
test("can set default values", async () => {
// class TestWidget extends Component {
// static defaultProps = { p: 4 };
// static template = xml`
`;
// }
// class Parent extends Component {
// static template = xml`
`;
// static components = { TestWidget };
// }
// const w = new Parent();
// await w.mount(fixture);
// expect(fixture.innerHTML).toBe("");
});
test("default values are also set whenever component is updated", async () => {
// class TestWidget extends Widget {
// static template = xml`
`;
// static defaultProps = { p: 4 };
// }
// class Parent extends Widget {
// static template = xml`
`;
// static components = { TestWidget };
// state: any = useState({ p: 1 });
// }
// const w = new Parent();
// await w.mount(fixture);
// expect(fixture.innerHTML).toBe("");
// w.state.p = undefined;
// await nextTick();
// expect(fixture.innerHTML).toBe("");
});
test("can set default required boolean values", async () => {
// class TestWidget extends Widget {
// static props = ["p", "q"];
// static defaultProps = { p: true, q: false };
// static template = xml`heyhey`;
// }
// class App extends Widget {
// static template = xml`
`;
// static components = { TestWidget };
// }
// const w = new App(undefined, {});
// await w.mount(fixture);
// expect(fixture.innerHTML).toBe("heyhey
");
});
});