[IMP] qweb: compiler: support t-key on node and component without t-foreach

This commit is contained in:
Lucas Perais (lpe)
2021-11-04 13:46:49 +01:00
committed by Aaron Bohy
parent 16f1e2c237
commit 7c04cc425e
19 changed files with 722 additions and 64 deletions
@@ -581,7 +581,8 @@ exports[`basics reconciliation alg is not confused in some specific situation 2`
return function template(ctx, node, key = \\"\\") {
let b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
let b3 = component(\`Child\`, {}, key + \`__2\`, node, ctx);
const tKey_2 = 4;
let b3 = toggler(tKey_2, component(\`Child\`, {}, tKey_2 + key + \`__3\`, node, ctx));
return block1([], [b2, b3]);
}
}"
@@ -640,8 +641,10 @@ exports[`basics same t-keys in two different places 2`] = `
let block1 = createBlock(\`<div><div><block-child-0/></div><div><block-child-1/></div></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2 = component(\`Child\`, {blip: '1'}, key + \`__1\`, node, ctx);
let b3 = component(\`Child\`, {blip: '2'}, key + \`__2\`, node, ctx);
const tKey_1 = 1;
let b2 = toggler(tKey_1, component(\`Child\`, {blip: '1'}, tKey_1 + key + \`__2\`, node, ctx));
const tKey_3 = 1;
let b3 = toggler(tKey_3, component(\`Child\`, {blip: '2'}, tKey_3 + key + \`__4\`, node, ctx));
return block1([], [b2, b3]);
}
}"
@@ -889,14 +892,15 @@ exports[`basics t-key on a component with t-if, and a sibling component 2`] = `
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
let block1 = createBlock(\`<div><block-child-0/><block-child-0/><block-child-1/></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2,b3;
if (false) {
b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
const tKey_1 = 'str';
b2 = toggler(tKey_1, component(\`Child\`, {}, tKey_1 + key + \`__2\`, node, ctx));
}
b3 = component(\`Child\`, {}, key + \`__2\`, node, ctx);
b3 = component(\`Child\`, {}, key + \`__3\`, node, ctx);
return block1([], [b2, b3]);
}
}"
@@ -18,7 +18,7 @@ exports[`calling render in destroy 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, inTCall, shallowEqual, setContextValue } = helpers;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -33,7 +33,7 @@ exports[`calling render in destroy 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, shallowEqual } = helpers;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
return function template(ctx, node, key = \\"\\") {
return component(\`C\`, {fromA: ctx['props'].fromA}, key + \`__1\`, node, ctx);
@@ -45,10 +45,11 @@ exports[`calling render in destroy 3`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, shallowEqual } = helpers;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
return function template(ctx, node, key = \\"\\") {
return component(\`B\`, {fromA: ctx['state']}, key + \`__1\`, node, ctx);
const tKey_1 = ctx['key'];
return toggler(tKey_1, component(\`B\`, {fromA: ctx['state']}, tKey_1 + key + \`__2\`, node, ctx));
}
}"
`;
@@ -1159,12 +1160,13 @@ exports[`two renderings initiated between willPatch and patched 2`] = `
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block1 = createBlock(\`<div><block-child-0/><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2;
if (ctx['state'].flag) {
b2 = component(\`Panel\`, {val: ctx['state'].panel}, key + \`__1\`, node, ctx);
const tKey_1 = 'panel_'+ctx['state'].panel;
b2 = toggler(tKey_1, component(\`Panel\`, {val: ctx['state'].panel}, tKey_1 + key + \`__2\`, node, ctx));
}
return block1([], [b2]);
}
@@ -79,8 +79,9 @@ exports[`t-component can use dynamic components (the class) if given (with diffe
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
return function template(ctx, node, key = \\"\\") {
let Comp2 = ctx['myComponent'];
return toggler(Comp2, component(Comp2, {}, key + \`__1\`, node, ctx));
const tKey_1 = ctx['state'].child;
let Comp3 = ctx['myComponent'];
return toggler(tKey_1, toggler(Comp3, component(Comp3, {}, tKey_1 + key + \`__2\`, node, ctx)));
}
}"
`;
@@ -120,8 +121,9 @@ exports[`t-component can use dynamic components (the class) if given 3`] = `
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
return function template(ctx, node, key = \\"\\") {
let Comp2 = ctx['myComponent'];
return toggler(Comp2, component(Comp2, {}, key + \`__1\`, node, ctx));
const tKey_1 = ctx['state'].child;
let Comp3 = ctx['myComponent'];
return toggler(tKey_1, toggler(Comp3, component(Comp3, {}, tKey_1 + key + \`__2\`, node, ctx)));
}
}"
`;
@@ -281,6 +281,43 @@ exports[`list of components sub components with some state rendered in a loop 2`
}"
`;
exports[`list of components switch component position 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let d1 = ctx['props'].key;
return block1([d1]);
}
}"
`;
exports[`list of components switch component position 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['clist']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`c\`] = v_block2[i1];
let key1 = ctx['c'];
c_block2[i1] = withKey(component(\`Child\`, {key: ctx['c']}, key + \`__1__\${key1}\`, node, ctx), key1);
}
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`list of components t-foreach with t-component, and update 1`] = `
"function anonymous(bdom, helpers
) {
@@ -0,0 +1,236 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`t-key t-foreach with t-key switch component position 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let d1 = ctx['props'].key;
return block1([d1]);
}
}"
`;
exports[`t-key t-foreach with t-key switch component position 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['clist']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`c\`] = v_block2[i1];
let key1 = ctx['c'];
const tKey_1 = ctx['key1'];
c_block2[i1] = withKey(component(\`Child\`, {key: ctx['c']+ctx['key1']}, tKey_1 + key + \`__2__\${key1}\`, node, ctx), tKey_1 + key1);
}
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-key t-key on Component 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let d1 = ctx['props'].key;
return block1([d1]);
}
}"
`;
exports[`t-key t-key on Component 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const tKey_1 = ctx['key'];
let b2 = toggler(tKey_1, component(\`Child\`, {key: ctx['key']}, tKey_1 + key + \`__2\`, node, ctx));
return block1([], [b2]);
}
}"
`;
exports[`t-key t-key on Component as a function 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let d1 = ctx['props'].key;
return block1([d1]);
}
}"
`;
exports[`t-key t-key on Component as a function 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const tKey_1 = ctx['key'];
let b2 = toggler(tKey_1, component(\`Child\`, {key: ctx['key']}, tKey_1 + key + \`__2\`, node, ctx));
return block1([], [b2]);
}
}"
`;
exports[`t-key t-key on multiple Components 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let d1 = ctx['props'].key;
return block1([d1]);
}
}"
`;
exports[`t-key t-key on multiple Components 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<span><block-child-0/><block-child-1/></span>\`);
return function template(ctx, node, key = \\"\\") {
const tKey_1 = ctx['key1'];
let b2 = toggler(tKey_1, component(\`Child\`, {key: ctx['key1']}, tKey_1 + key + \`__2\`, node, ctx));
const tKey_3 = ctx['key2'];
let b3 = toggler(tKey_3, component(\`Child\`, {key: ctx['key2']}, tKey_3 + key + \`__4\`, node, ctx));
return block1([], [b2, b3]);
}
}"
`;
exports[`t-key t-key on multiple Components with t-call 1 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let d1 = ctx['props'].key;
return block1([d1]);
}
}"
`;
exports[`t-key t-key on multiple Components with t-call 1 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
return function template(ctx, node, key = \\"\\") {
const tKey_1 = ctx['key'];
return toggler(tKey_1, component(\`Child\`, {key: ctx['key']}, tKey_1 + key + \`__2\`, node, ctx));
}
}"
`;
exports[`t-key t-key on multiple Components with t-call 1 3`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
const callTemplate_2 = getTemplate(\`calledTemplate\`);
const callTemplate_4 = getTemplate(\`calledTemplate\`);
let block1 = createBlock(\`<span><block-child-0/><block-child-1/></span>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
ctx = Object.create(ctx);
ctx[isBoundary] = 1;
setContextValue(ctx, \\"key\\", ctx['key1']);
let b2 = callTemplate_2(ctx, node, key + \`__1\`);
ctx = ctx.__proto__;
ctx = Object.create(ctx);
ctx[isBoundary] = 1;
setContextValue(ctx, \\"key\\", ctx['key2']);
let b3 = callTemplate_4(ctx, node, key + \`__3\`);
return block1([], [b2, b3]);
}
}"
`;
exports[`t-key t-key on multiple Components with t-call 2 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let d1 = ctx['props'].key;
return block1([d1]);
}
}"
`;
exports[`t-key t-key on multiple Components with t-call 2 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
return function template(ctx, node, key = \\"\\") {
const tKey_1 = ctx['key1'];
let b2 = toggler(tKey_1, component(\`Child\`, {key: ctx['key1']}, tKey_1 + key + \`__2\`, node, ctx));
const tKey_3 = ctx['key2'];
let b3 = toggler(tKey_3, component(\`Child\`, {key: ctx['key2']}, tKey_3 + key + \`__4\`, node, ctx));
return multi([b2, b3]);
}
}"
`;
exports[`t-key t-key on multiple Components with t-call 2 3`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
const callTemplate_2 = getTemplate(\`calledTemplate\`);
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
let b2 = callTemplate_2(ctx, node, key + \`__1\`);
return block1([], [b2]);
}
}"
`;
+24 -15
View File
@@ -2108,7 +2108,7 @@ test("concurrent renderings scenario 16", async () => {
// });
// TODO: unskip when t-key is reimplemented properly
test.skip("calling render in destroy", async () => {
test("calling render in destroy", async () => {
const steps: any[] = [];
let a: any = null;
@@ -2165,7 +2165,7 @@ test.skip("calling render in destroy", async () => {
// this nextTick is critical, otherwise jest may silently swallow errors
await nextTick();
expect(steps).toBe(["B:mounted", "B:willUnmount", "B:mounted"]);
expect(steps).toStrictEqual(["B:mounted", "B:willUnmount", "B:mounted"]);
expect(fixture.innerHTML).toBe("<div>A</div>");
});
@@ -2302,7 +2302,7 @@ test("render method wait until rendering is done", async () => {
test("two renderings initiated between willPatch and patched", async () => {
let parent: any = null;
let steps: string[] = [];
const steps: string[] = [];
class Panel extends Component {
static template = xml`<abc><t t-esc="props.val"/></abc>`;
@@ -2325,17 +2325,8 @@ test("two renderings initiated between willPatch and patched", async () => {
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("<div><abc>Panel1</abc></div>");
parent.state.panel = "Panel2";
await nextTick();
expect(fixture.innerHTML).toBe("<div><abc>Panel2</abc></div>");
parent.state.flag = false;
await nextTick();
expect(fixture.innerHTML).toBe("<div></div>");
expect(steps).toEqual([
"Parent:setup",
"Parent:willStart",
@@ -2345,13 +2336,31 @@ test("two renderings initiated between willPatch and patched", async () => {
"Panel:render",
"Panel:mounted",
"Parent:mounted",
]);
steps.length = 0;
parent.state.panel = "Panel2";
await nextTick();
expect(fixture.innerHTML).toBe("<div><abc>Panel2</abc></div>");
expect(steps).toEqual([
"Parent:render",
"Panel:willUpdateProps",
"Panel:setup",
"Panel:willStart",
"Panel:render",
"Parent:willPatch",
"Panel:willPatch",
"Panel:patched",
"Panel:willUnmount",
"Panel:destroyed",
"Panel:mounted",
"Parent:patched",
]);
steps.length = 0;
parent.state.flag = false;
await nextTick();
expect(fixture.innerHTML).toBe("<div></div>");
expect(steps).toEqual([
"Parent:render",
"Parent:willPatch",
"Panel:willUnmount",
+5 -10
View File
@@ -43,8 +43,7 @@ describe("basics", () => {
expect(fixture.innerHTML).toBe("");
expect(status(parent)).toBe("destroyed");
expect(error).toBeDefined();
const regexp =
/Cannot read properties of undefined \(reading 'this'\)|Cannot read property 'this' of undefined/g;
const regexp = /Cannot read properties of undefined \(reading 'this'\)|Cannot read property 'this' of undefined/g;
expect(error.message).toMatch(regexp);
});
@@ -113,8 +112,7 @@ describe.skip("errors and promises", () => {
error = e;
}
expect(error).toBeDefined();
const regexp =
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
expect(error.message).toMatch(regexp);
expect(console.error).toBeCalledTimes(0);
@@ -227,8 +225,7 @@ describe.skip("errors and promises", () => {
error = e;
}
expect(error).toBeDefined();
const regexp =
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
expect(error.message).toMatch(regexp);
expect(console.error).toBeCalledTimes(0);
@@ -254,8 +251,7 @@ describe.skip("errors and promises", () => {
error = e;
}
expect(error).toBeDefined();
const regexp =
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
expect(error.message).toMatch(regexp);
expect(console.error).toBeCalledTimes(0);
@@ -278,8 +274,7 @@ describe.skip("errors and promises", () => {
error = e;
}
expect(error).toBeDefined();
const regexp =
/Cannot read properties of undefined \(reading 'y'\)|Cannot read property 'y' of undefined/g;
const regexp = /Cannot read properties of undefined \(reading 'y'\)|Cannot read property 'y' of undefined/g;
expect(error.message).toMatch(regexp);
});
+1 -2
View File
@@ -341,8 +341,7 @@ describe("style and class handling", () => {
error = e;
}
expect(error).toBeDefined();
const regexp =
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
expect(error.message).toMatch(regexp);
expect(fixture.innerHTML).toBe("");
});
+28
View File
@@ -260,4 +260,32 @@ describe("list of components", () => {
await nextTick(); // wait for changes triggered in mounted to be applied
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
});
test("switch component position", async () => {
const childInstances = [];
class Child extends Component {
static template = xml`<div t-esc="props.key"></div>`;
setup() {
childInstances.push(this);
}
}
class Parent extends Component {
static components = { Child };
static template = xml`<span>
<t t-foreach="clist" t-as="c" t-key="c">
<Child key="c"/>
</t>
</span>`;
clist = [1, 2];
}
const parent = await mount(Parent, fixture);
expect((parent.el as HTMLElement).innerHTML).toBe("<div>1</div><div>2</div>");
parent.clist = [2, 1];
await parent.render();
expect((parent.el as HTMLElement).innerHTML).toBe("<div>2</div><div>1</div>");
expect(childInstances.length).toBe(2);
});
});
+208
View File
@@ -0,0 +1,208 @@
import { snapshotEverything, makeTestFixture, addTemplate } from "../helpers";
import { Component, mount, xml } from "../../src/index";
snapshotEverything();
let fixture: HTMLElement;
beforeEach(() => {
fixture = makeTestFixture();
});
describe("t-key", () => {
test("t-key on Component", async () => {
let childInstance = null;
class Child extends Component {
static template = xml`<div t-esc="props.key"></div>`;
setup() {
childInstance = this;
}
}
class Parent extends Component {
static components = { Child };
static template = xml`<span><Child t-key="key" key="key" /></span>`;
key = 1;
}
const parent = await mount(Parent, fixture);
expect((parent.el as HTMLElement).innerHTML).toBe("<div>1</div>");
const oldChild = childInstance;
parent.key = 2;
await parent.render();
expect((parent.el as HTMLElement).innerHTML).toBe("<div>2</div>");
expect(oldChild === childInstance).toBeFalsy();
});
test("t-key on Component as a function", async () => {
let childInstance = null;
class Child extends Component {
static template = xml`<div t-esc="props.key"></div>`;
setup() {
childInstance = this;
}
}
let keyCalls = 0;
let __key = 1;
class Parent extends Component {
static components = { Child };
static template = xml`<span><Child t-key="key" key="key" /></span>`;
get key() {
keyCalls++;
return __key;
}
}
const parent = await mount(Parent, fixture);
expect((parent.el as HTMLElement).innerHTML).toBe("<div>1</div>");
expect(keyCalls).toBe(2); // one for t-key, the other for the props
const oldChild = childInstance;
__key = 2;
await parent.render();
expect((parent.el as HTMLElement).innerHTML).toBe("<div>2</div>");
expect(oldChild === childInstance).toBeFalsy();
expect(keyCalls).toBe(4);
});
test("t-key on multiple Components", async () => {
const childInstances = [];
class Child extends Component {
static template = xml`<div t-esc="props.key"></div>`;
setup() {
childInstances.push(this);
}
}
class Parent extends Component {
static components = { Child };
static template = xml`<span>
<Child t-key="key1" key="key1" />
<Child t-key="key2" key="key2" />
</span>`;
key1 = 1;
key2 = 2;
}
const parent = await mount(Parent, fixture);
expect((parent.el as HTMLElement).innerHTML).toBe("<div>1</div><div>2</div>");
parent.key1 = 2;
parent.key2 = 1;
await parent.render();
expect((parent.el as HTMLElement).innerHTML).toBe("<div>2</div><div>1</div>");
expect(childInstances.length).toBe(4);
});
test("t-key on multiple Components with t-call 1", async () => {
const childInstances = [];
class Child extends Component {
static template = xml`<div t-esc="props.key"></div>`;
setup() {
childInstances.push(this);
}
}
addTemplate("calledTemplate", `<Child t-key="key" key="key" />`);
class Parent extends Component {
static components = { Child };
static template = xml`<span>
<t t-call="calledTemplate"><t t-set="key" t-value="key1" /></t>
<t t-call="calledTemplate"><t t-set="key" t-value="key2" /></t>
</span>`;
key1 = 1;
key2 = 2;
}
const parent = await mount(Parent, fixture);
expect((parent.el as HTMLElement).innerHTML).toBe("<div>1</div><div>2</div>");
parent.key1 = 2;
parent.key2 = 1;
await parent.render();
expect((parent.el as HTMLElement).innerHTML).toBe("<div>2</div><div>1</div>");
expect(childInstances.length).toBe(4);
});
test("t-key on multiple Components with t-call 2", async () => {
const childInstances = [];
class Child extends Component {
static template = xml`<div t-esc="props.key"></div>`;
setup() {
childInstances.push(this);
}
}
addTemplate(
"calledTemplate",
`<Child t-key="key1" key="key1" /><Child t-key="key2" key="key2" />`
);
class Parent extends Component {
static components = { Child };
static template = xml`<span>
<t t-call="calledTemplate" />
</span>`;
key1 = 1;
key2 = 2;
}
const parent = await mount(Parent, fixture);
expect((parent.el as HTMLElement).innerHTML).toBe("<div>1</div><div>2</div>");
parent.key1 = 2;
parent.key2 = 1;
await parent.render();
expect((parent.el as HTMLElement).innerHTML).toBe("<div>2</div><div>1</div>");
expect(childInstances.length).toBe(4);
});
test("t-foreach with t-key switch component position", async () => {
const childInstances = [];
class Child extends Component {
static template = xml`<div t-esc="props.key"></div>`;
setup() {
childInstances.push(this);
}
}
class Parent extends Component {
static components = { Child };
static template = xml`<span>
<t t-foreach="clist" t-as="c" t-key="c">
<Child key="c + key1" t-key="key1"/>
</t>
</span>`;
key1 = "key1";
clist = [1, 2];
}
const parent = await mount(Parent, fixture);
expect((parent.el as HTMLElement).innerHTML).toBe("<div>1key1</div><div>2key1</div>");
parent.clist = [2, 1];
await parent.render();
expect((parent.el as HTMLElement).innerHTML).toBe("<div>2key1</div><div>1key1</div>");
expect(childInstances.length).toBe(2);
childInstances.length = 0;
parent.clist = [1, 2];
parent.key1 = "key2";
await parent.render();
expect((parent.el as HTMLElement).innerHTML).toBe("<div>1key2</div><div>2key2</div>");
expect(childInstances.length).toBe(2);
});
});