From 17fb33475c65f8be5dc04ecf810d7293b4273339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Mon, 26 Sep 2022 15:02:43 +0200 Subject: [PATCH] [FIX] props validation: make it work through slots A recent commit fixes the props validation code to make it work regardless of the rendering context (important with the recent t-call-context directive). Unfortunately, it then breaks props validation through slots, because it assumed that the parent node in the virtual node was the parent of the component, but it is not necessarily true. To fix this, we can use a simple property of the template functions: they are bound to the current instance of the component, so we can simply use "this" --- src/compiler/code_generator.ts | 2 +- src/runtime/app.ts | 2 +- src/runtime/template_helpers.ts | 6 +- .../__snapshots__/error_handling.test.ts.snap | 2 +- .../__snapshots__/lifecycle.test.ts.snap | 2 +- .../props_validation.test.ts.snap | 174 +++++++++++------- .../__snapshots__/t_call.test.ts.snap | 4 +- .../__snapshots__/t_foreach.test.ts.snap | 2 +- tests/components/props_validation.test.ts | 26 +++ 9 files changed, 140 insertions(+), 80 deletions(-) diff --git a/src/compiler/code_generator.ts b/src/compiler/code_generator.ts index 6d41f67a..10e35666 100644 --- a/src/compiler/code_generator.ts +++ b/src/compiler/code_generator.ts @@ -1188,7 +1188,7 @@ export class CodeGenerator { } if (this.dev) { - this.addLine(`helpers.validateProps(${expr}, ${propVar!}, node);`); + this.addLine(`helpers.validateProps(${expr}, ${propVar!}, this);`); } if (block && (ctx.forceNewBlock === false || ctx.tKeyExpr)) { diff --git a/src/runtime/app.ts b/src/runtime/app.ts index f548088f..736f221d 100644 --- a/src/runtime/app.ts +++ b/src/runtime/app.ts @@ -66,7 +66,7 @@ export class App< mount(target: HTMLElement, options?: MountOptions): Promise & InstanceType> { App.validateTarget(target); if (this.dev) { - validateProps(this.Root, this.props, { app: this }); + validateProps(this.Root, this.props, { __owl__: { app: this } }); } const node = this.makeNode(this.Root, this.props); const prom = this.mountNode(node, target, options); diff --git a/src/runtime/template_helpers.ts b/src/runtime/template_helpers.ts index cf7a9181..b87e6b1e 100644 --- a/src/runtime/template_helpers.ts +++ b/src/runtime/template_helpers.ts @@ -207,11 +207,11 @@ function multiRefSetter(refs: RefMap, name: string): RefSetter { * visit recursively the props and all the children to check if they are valid. * This is why it is only done in 'dev' mode. */ -export function validateProps

(name: string | ComponentConstructor

, props: P, node?: any) { +export function validateProps

(name: string | ComponentConstructor

, props: P, comp?: any) { const ComponentClass = typeof name !== "string" ? name - : (node.component.constructor.components[name] as ComponentConstructor

| undefined); + : (comp.constructor.components[name] as ComponentConstructor

| undefined); if (!ComponentClass) { // this is an error, wrong component. We silently return here instead so the @@ -221,7 +221,7 @@ export function validateProps

(name: string | ComponentConstructor

, props: const schema = ComponentClass.props; if (!schema) { - if (node.app.warnIfNoStaticProps) { + if (comp.__owl__.app.warnIfNoStaticProps) { console.warn(`Component '${ComponentClass.name}' does not have a static props description`); } return; diff --git a/tests/components/__snapshots__/error_handling.test.ts.snap b/tests/components/__snapshots__/error_handling.test.ts.snap index 9e48ca71..92fc7779 100644 --- a/tests/components/__snapshots__/error_handling.test.ts.snap +++ b/tests/components/__snapshots__/error_handling.test.ts.snap @@ -20,7 +20,7 @@ exports[`basics display a nice error if it cannot find component (in dev mode) 1 return function template(ctx, node, key = \\"\\") { const props1 = {}; - helpers.validateProps(\`SomeMispelledComponent\`, props1, node); + helpers.validateProps(\`SomeMispelledComponent\`, props1, this); return comp1(props1, key + \`__1\`, node, this, null); } }" diff --git a/tests/components/__snapshots__/lifecycle.test.ts.snap b/tests/components/__snapshots__/lifecycle.test.ts.snap index 8068056b..285e939d 100644 --- a/tests/components/__snapshots__/lifecycle.test.ts.snap +++ b/tests/components/__snapshots__/lifecycle.test.ts.snap @@ -704,7 +704,7 @@ exports[`lifecycle hooks timeout in onWillUpdateProps emits a warning 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {prop: ctx['state'].prop}; - helpers.validateProps(\`Child\`, props1, node); + helpers.validateProps(\`Child\`, props1, this); return comp1(props1, key + \`__1\`, node, this, null); } }" diff --git a/tests/components/__snapshots__/props_validation.test.ts.snap b/tests/components/__snapshots__/props_validation.test.ts.snap index 37654493..9ae2f135 100644 --- a/tests/components/__snapshots__/props_validation.test.ts.snap +++ b/tests/components/__snapshots__/props_validation.test.ts.snap @@ -8,7 +8,7 @@ exports[`default props a default prop cannot be defined on a mandatory prop 1`] return function template(ctx, node, key = \\"\\") { const props1 = {}; - helpers.validateProps(\`Child\`, props1, node); + helpers.validateProps(\`Child\`, props1, this); return comp1(props1, key + \`__1\`, node, this, null); } }" @@ -24,7 +24,7 @@ exports[`default props can set default boolean values 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -61,7 +61,7 @@ exports[`default props can set default values 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -92,7 +92,7 @@ exports[`default props default values are also set whenever component is updated return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['state'].p}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -121,7 +121,7 @@ exports[`props validation can specify that additional props are allowed (array) return function template(ctx, node, key = \\"\\") { const props1 = {message: 'm',otherProp: 'o'}; - helpers.validateProps(\`Child\`, props1, node); + helpers.validateProps(\`Child\`, props1, this); return comp1(props1, key + \`__1\`, node, this, null); } }" @@ -148,7 +148,7 @@ exports[`props validation can specify that additional props are allowed (object) return function template(ctx, node, key = \\"\\") { const props1 = {message: 'm',otherProp: 'o'}; - helpers.validateProps(\`Child\`, props1, node); + helpers.validateProps(\`Child\`, props1, this); return comp1(props1, key + \`__1\`, node, this, null); } }" @@ -177,7 +177,7 @@ exports[`props validation can validate a prop with multiple types 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -207,7 +207,7 @@ exports[`props validation can validate a prop with multiple types 3`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -237,7 +237,7 @@ exports[`props validation can validate a prop with multiple types 5`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -254,7 +254,7 @@ exports[`props validation can validate an array with given primitive type 1`] = return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -284,7 +284,7 @@ exports[`props validation can validate an array with given primitive type 3`] = return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -314,7 +314,7 @@ exports[`props validation can validate an array with given primitive type 5`] = return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -331,7 +331,7 @@ exports[`props validation can validate an array with given primitive type 6`] = return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -348,7 +348,7 @@ exports[`props validation can validate an array with multiple sub element types return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -378,7 +378,7 @@ exports[`props validation can validate an array with multiple sub element types return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -408,7 +408,7 @@ exports[`props validation can validate an array with multiple sub element types return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -438,7 +438,7 @@ exports[`props validation can validate an array with multiple sub element types return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -455,7 +455,7 @@ exports[`props validation can validate an object with simple shape 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -485,7 +485,7 @@ exports[`props validation can validate an object with simple shape 3`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -502,7 +502,7 @@ exports[`props validation can validate an object with simple shape 4`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -519,7 +519,7 @@ exports[`props validation can validate an object with simple shape 5`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -536,7 +536,7 @@ exports[`props validation can validate an optional props 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -566,7 +566,7 @@ exports[`props validation can validate an optional props 3`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -596,7 +596,7 @@ exports[`props validation can validate an optional props 5`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -613,7 +613,7 @@ exports[`props validation can validate recursively complicated prop def 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -643,7 +643,7 @@ exports[`props validation can validate recursively complicated prop def 3`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -673,13 +673,47 @@ exports[`props validation can validate recursively complicated prop def 5`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } }" `; +exports[`props validation can validate through slots 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { markRaw } = helpers; + const comp1 = app.createComponent(\`Child\`, true, false, false, true); + const comp2 = app.createComponent(\`Wrapper\`, true, true, false, true); + + function slot1(ctx, node, key = \\"\\") { + const props1 = {}; + helpers.validateProps(\`Child\`, props1, this); + return comp1(props1, key + \`__1\`, node, this, null); + } + + return function template(ctx, node, key = \\"\\") { + const props2 = {slots: markRaw({'default': {__render: slot1, __ctx: ctx}})}; + helpers.validateProps(\`Wrapper\`, props2, this); + return comp2(props2, key + \`__2\`, node, this, null); + } +}" +`; + +exports[`props validation can validate through slots 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { callSlot } = helpers; + + return function template(ctx, node, key = \\"\\") { + return callSlot(ctx, node, key, 'default', false, {}); + } +}" +`; + exports[`props validation default values are applied before validating props at update 1`] = ` "function anonymous(app, bdom, helpers ) { @@ -690,7 +724,7 @@ exports[`props validation default values are applied before validating props at return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['state'].p}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -721,7 +755,7 @@ exports[`props validation missing required boolean prop causes an error 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -738,7 +772,7 @@ exports[`props validation mix of optional and mandatory 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {}; - helpers.validateProps(\`Child\`, props1, node); + helpers.validateProps(\`Child\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -755,7 +789,7 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] = return function template(ctx, node, key = \\"\\") { const props1 = {message: 1}; - helpers.validateProps(\`Child\`, props1, node); + helpers.validateProps(\`Child\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -786,7 +820,7 @@ exports[`props validation props are validated whenever component is updated 1`] return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['state'].p}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -817,7 +851,7 @@ exports[`props validation props: list of strings 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -834,7 +868,7 @@ exports[`props validation validate simple types 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -851,7 +885,7 @@ exports[`props validation validate simple types 2`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -881,7 +915,7 @@ exports[`props validation validate simple types 4`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -898,7 +932,7 @@ exports[`props validation validate simple types 5`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -915,7 +949,7 @@ exports[`props validation validate simple types 6`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -945,7 +979,7 @@ exports[`props validation validate simple types 8`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -962,7 +996,7 @@ exports[`props validation validate simple types 9`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -979,7 +1013,7 @@ exports[`props validation validate simple types 10`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1009,7 +1043,7 @@ exports[`props validation validate simple types 12`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1026,7 +1060,7 @@ exports[`props validation validate simple types 13`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1043,7 +1077,7 @@ exports[`props validation validate simple types 14`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1073,7 +1107,7 @@ exports[`props validation validate simple types 16`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1090,7 +1124,7 @@ exports[`props validation validate simple types 17`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1107,7 +1141,7 @@ exports[`props validation validate simple types 18`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1137,7 +1171,7 @@ exports[`props validation validate simple types 20`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1154,7 +1188,7 @@ exports[`props validation validate simple types 21`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1171,7 +1205,7 @@ exports[`props validation validate simple types 22`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1201,7 +1235,7 @@ exports[`props validation validate simple types 24`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1218,7 +1252,7 @@ exports[`props validation validate simple types, alternate form 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1235,7 +1269,7 @@ exports[`props validation validate simple types, alternate form 2`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1265,7 +1299,7 @@ exports[`props validation validate simple types, alternate form 4`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1282,7 +1316,7 @@ exports[`props validation validate simple types, alternate form 5`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1299,7 +1333,7 @@ exports[`props validation validate simple types, alternate form 6`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1329,7 +1363,7 @@ exports[`props validation validate simple types, alternate form 8`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1346,7 +1380,7 @@ exports[`props validation validate simple types, alternate form 9`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1363,7 +1397,7 @@ exports[`props validation validate simple types, alternate form 10`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1393,7 +1427,7 @@ exports[`props validation validate simple types, alternate form 12`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1410,7 +1444,7 @@ exports[`props validation validate simple types, alternate form 13`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1427,7 +1461,7 @@ exports[`props validation validate simple types, alternate form 14`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1457,7 +1491,7 @@ exports[`props validation validate simple types, alternate form 16`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1474,7 +1508,7 @@ exports[`props validation validate simple types, alternate form 17`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1491,7 +1525,7 @@ exports[`props validation validate simple types, alternate form 18`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1521,7 +1555,7 @@ exports[`props validation validate simple types, alternate form 20`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1538,7 +1572,7 @@ exports[`props validation validate simple types, alternate form 21`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1555,7 +1589,7 @@ exports[`props validation validate simple types, alternate form 22`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1585,7 +1619,7 @@ exports[`props validation validate simple types, alternate form 24`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {p: ctx['p']}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } @@ -1602,7 +1636,7 @@ exports[`props validation validation is only done in dev mode 1`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {}; - helpers.validateProps(\`SubComp\`, props1, node); + helpers.validateProps(\`SubComp\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); return block1([], [b2]); } diff --git a/tests/components/__snapshots__/t_call.test.ts.snap b/tests/components/__snapshots__/t_call.test.ts.snap index 7dc6078f..9debc214 100644 --- a/tests/components/__snapshots__/t_call.test.ts.snap +++ b/tests/components/__snapshots__/t_call.test.ts.snap @@ -466,10 +466,10 @@ exports[`t-call t-call with t-call-context and subcomponent, in dev mode 2`] = ` return function template(ctx, node, key = \\"\\") { const props1 = {name: ctx['aab']}; - helpers.validateProps(\`Child\`, props1, node); + helpers.validateProps(\`Child\`, props1, this); const b2 = comp1(props1, key + \`__1\`, node, this, null); const props2 = {name: ctx['lpe']}; - helpers.validateProps(\`Child\`, props2, node); + helpers.validateProps(\`Child\`, props2, this); const b3 = comp2(props2, key + \`__2\`, node, this, null); return multi([b2, b3]); } diff --git a/tests/components/__snapshots__/t_foreach.test.ts.snap b/tests/components/__snapshots__/t_foreach.test.ts.snap index 9e8ef827..34c3f1df 100644 --- a/tests/components/__snapshots__/t_foreach.test.ts.snap +++ b/tests/components/__snapshots__/t_foreach.test.ts.snap @@ -56,7 +56,7 @@ exports[`list of components crash on duplicate key in dev mode 1`] = ` if (keys1.has(key1)) { throw new OwlError(\`Got duplicate key in t-foreach: \${key1}\`)} keys1.add(key1); const props1 = {}; - helpers.validateProps(\`Child\`, props1, node); + helpers.validateProps(\`Child\`, props1, this); c_block1[i1] = withKey(comp1(props1, key + \`__1__\${key1}\`, node, this, null), key1); } return list(c_block1); diff --git a/tests/components/props_validation.test.ts b/tests/components/props_validation.test.ts index 12f6a894..55327611 100644 --- a/tests/components/props_validation.test.ts +++ b/tests/components/props_validation.test.ts @@ -780,6 +780,32 @@ describe("props validation", () => { // we just check that it doesn't throw await expect(mount(Parent, fixture, { dev: true })).resolves.toEqual(expect.anything()); }); + + test("can validate through slots", async () => { + class Child extends Component { + static props = ["message"]; + static template = xml`

hey
`; + } + + class Wrapper extends Component { + static template = xml``; + } + + class Parent extends Component { + static components = { Child, Wrapper }; + static template = xml``; + } + + const app = new App(Parent, { test: true }); + let error: OwlError | undefined; + const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); + await expect(nextAppError(app)).resolves.toThrow( + "Invalid props for component 'Child': 'message' is missing" + ); + await mountProm; + expect(error!).toBeDefined(); + expect(error!.message).toBe("Invalid props for component 'Child': 'message' is missing"); + }); }); //------------------------------------------------------------------------------