mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] components: re-add a bunch of components tests from owl 1
Some tests are skipped because they rely on not-yet-implemented features.
This commit is contained in:
committed by
Géry Debongnie
parent
c9cc789f1d
commit
642ecf0ccd
@@ -328,6 +328,44 @@ exports[`basics do not remove previously rendered dom if not necessary, variatio
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics don't fallback to component's registry if widget defined in the instance's context 1`] = `
|
||||
"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 block1 = createBlock(\`<span>ChildB</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics don't fallback to component's registry if widget defined in the instance's context 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;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics don't fallback to component's registry if widget defined in the instance's context 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;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics has no el after creation 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
@@ -400,6 +438,45 @@ exports[`basics higher order components parent and child 4`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics list of two sub components inside other nodes 1`] = `
|
||||
"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 block1 = createBlock(\`<span>asdf</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics list of two sub components inside other nodes 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 block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block3 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
const [k2, v2, l2, c2] = prepareList(ctx['state'].blips);
|
||||
for (let i1 = 0; i1 < l2; i1++) {
|
||||
ctx[\`blip\`] = v2[i1];
|
||||
let key1 = ctx['blip'].id;
|
||||
let b4 = component(\`SubWidget\`, {}, key + \`__1__\${key1}\`, node, ctx);
|
||||
let b5 = component(\`SubWidget\`, {}, key + \`__2__\${key1}\`, node, ctx);
|
||||
c2[i1] = withKey(block3([], [b4, b5]), key1);
|
||||
}
|
||||
let b2 = list(c2);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics parent, child and grandchild 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
@@ -482,6 +559,35 @@ exports[`basics reconciliation alg is not confused in some specific situation 2`
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics rerendering a widget with a sub widget 1`] = `
|
||||
"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 block1 = createBlock(\`<div><block-text-0/><button block-handler-1=\\"click\\">Inc</button></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let d1 = ctx['state'].counter;
|
||||
const v1 = ctx['state'];
|
||||
let d2 = (e) => {const res = (() => { return v1.counter++ })(); if (typeof res === 'function') { res(e) }};
|
||||
return block1([d1, d2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics rerendering a widget with a sub widget 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;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return component(\`Counter\`, {}, key + \`__1\`, node, ctx);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics same t-keys in two different places 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
@@ -557,6 +663,47 @@ exports[`basics some simple sanity checks (el/status) 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics sub components between t-ifs 1`] = `
|
||||
"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 block1 = createBlock(\`<span>child</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics sub components between t-ifs 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 block1 = createBlock(\`<div><block-child-0/><block-child-1/><span><block-child-2/></span><block-child-3/></div>\`);
|
||||
let block2 = createBlock(\`<h1>hey</h1>\`);
|
||||
let block3 = createBlock(\`<h2>noo</h2>\`);
|
||||
let block5 = createBlock(\`<span>test</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2,b3,b4,b5;
|
||||
if (ctx['state'].flag) {
|
||||
b2 = block2();
|
||||
} else {
|
||||
b3 = block3();
|
||||
}
|
||||
b4 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||
if (ctx['state'].flag) {
|
||||
b5 = block5();
|
||||
}
|
||||
return block1([], [b2, b3, b4, b5]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`basics t-elif works with t-component 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
|
||||
@@ -479,6 +479,36 @@ exports[`lifecycle hooks patched hook is called after updating State 1`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`lifecycle hooks sub widget (inside sub node): hooks are correctly called 1`] = `
|
||||
"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 block1 = createBlock(\`<div/>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`lifecycle hooks sub widget (inside sub node): hooks are correctly called 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;
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2;
|
||||
if (ctx['state'].flag) {
|
||||
b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
|
||||
}
|
||||
return multi([b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`lifecycle hooks willPatch, patched hook are called on subsubcomponents, in proper order 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
|
||||
@@ -198,6 +198,36 @@ exports[`t-component switching dynamic component 3`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-component t-component not on a <t> node 1`] = `
|
||||
"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 block1 = createBlock(\`<span>1</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-component t-component not on a <t> node 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 block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let Comp2 = ctx['Child'];
|
||||
let b2 = toggler(Comp2, component(Comp2, {}, key + \`__1\`, node, ctx));
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`t-component t-component works in simple case 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
|
||||
@@ -39,6 +39,44 @@ exports[`list of components components in a node in a t-foreach 2`] = `
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`list of components list of sub components inside other nodes 1`] = `
|
||||
"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 block1 = createBlock(\`<span>asdf</span>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`list of components list of sub components inside other nodes 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 block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
let block3 = createBlock(\`<div><block-child-0/></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
ctx = Object.create(ctx);
|
||||
const [k2, v2, l2, c2] = prepareList(ctx['state'].blips);
|
||||
for (let i1 = 0; i1 < l2; i1++) {
|
||||
ctx[\`blip\`] = v2[i1];
|
||||
let key1 = ctx['blip'].id;
|
||||
let b4 = component(\`SubComponent\`, {}, key + \`__1__\${key1}\`, node, ctx);
|
||||
c2[i1] = withKey(block3([], [b4]), key1);
|
||||
}
|
||||
let b2 = list(c2);
|
||||
return block1([], [b2]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`list of components reconciliation alg works for t-foreach in t-foreach 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
|
||||
@@ -383,6 +383,27 @@ describe("basics", () => {
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div>1<button>Inc</button></div>");
|
||||
});
|
||||
// TODO: rename
|
||||
test("rerendering a widget with a sub widget", async () => {
|
||||
class Counter extends Component {
|
||||
static template = xml`
|
||||
<div><t t-esc="state.counter"/><button t-on-click="state.counter++">Inc</button></div>`;
|
||||
state = useState({
|
||||
counter: 0,
|
||||
});
|
||||
}
|
||||
class Parent extends Component {
|
||||
static template = xml`<Counter/>`;
|
||||
static components = { Counter };
|
||||
}
|
||||
const parent = await mount(Parent, fixture);
|
||||
const button = fixture.getElementsByTagName("button")[0];
|
||||
button.click();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div>1<button>Inc</button></div>");
|
||||
await parent.render();
|
||||
expect(fixture.innerHTML).toBe("<div>1<button>Inc</button></div>");
|
||||
});
|
||||
|
||||
test("can handle empty props", async () => {
|
||||
class Child extends Component {
|
||||
@@ -654,4 +675,68 @@ describe("basics", () => {
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>hey</span></div>");
|
||||
});
|
||||
|
||||
// TODO: implement scope lookup for components
|
||||
test.skip("don't fallback to component's registry if widget defined in the instance's context", async () => {
|
||||
class ChildA extends Component {
|
||||
static template = xml`<span>ChildA</span>`;
|
||||
}
|
||||
class ChildB extends Component {
|
||||
static template = xml`<span>ChildB</span>`;
|
||||
}
|
||||
class Parent extends Component {
|
||||
static template = xml`<Child/>`;
|
||||
static components = { Child: ChildA };
|
||||
Child = ChildB;
|
||||
}
|
||||
await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<span>ChildB</span>");
|
||||
});
|
||||
|
||||
test("sub components between t-ifs", async () => {
|
||||
// this confuses the patching algorithm...
|
||||
class Child extends Component {
|
||||
static template = xml`<span>child</span>`;
|
||||
}
|
||||
|
||||
class Parent extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<h1 t-if="state.flag">hey</h1>
|
||||
<h2 t-else="">noo</h2>
|
||||
<span><Child/></span>
|
||||
<t t-if="state.flag"><span>test</span></t>
|
||||
</div>`;
|
||||
static components = { Child };
|
||||
state = useState({ flag: false });
|
||||
}
|
||||
const parent = await mount(Parent, fixture);
|
||||
const child = Object.values(parent.__owl__.children)[0].component;
|
||||
expect(fixture.innerHTML).toBe(`<div><h2>noo</h2><span><span>child</span></span></div>`);
|
||||
|
||||
parent.state.flag = true;
|
||||
await nextTick();
|
||||
expect(Object.values(parent.__owl__.children)[0].component).toBe(child);
|
||||
expect(status(child)).toBe("mounted");
|
||||
expect(fixture.innerHTML).toBe(`<div><h1>hey</h1><span><span>child</span></span><span>test</span></div>`)
|
||||
});
|
||||
|
||||
test("list of two sub components inside other nodes", async () => {
|
||||
class SubWidget extends Component {
|
||||
static template = xml`<span>asdf</span>`;
|
||||
}
|
||||
class Parent extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<div t-foreach="state.blips" t-as="blip" t-key="blip.id">
|
||||
<SubWidget />
|
||||
<SubWidget />
|
||||
</div>
|
||||
</div>`;
|
||||
static components = { SubWidget };
|
||||
state = useState({ blips: [{ a: "a", id: 1 }] });
|
||||
}
|
||||
await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><div><span>asdf</span><span>asdf</span></div></div>");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,4 +39,25 @@ describe("basics", () => {
|
||||
/Cannot read properties of undefined \(reading 'this'\)|Cannot read property 'this' of undefined/g;
|
||||
expect(error.message).toMatch(regexp);
|
||||
});
|
||||
|
||||
test.skip("display a nice error if it cannot find component", async () => {
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
|
||||
class SomeComponent extends Component {}
|
||||
class Parent extends Component {
|
||||
static template = xml`<SomeMispelledComponent />`;
|
||||
static components = { SomeComponent };
|
||||
}
|
||||
let error;
|
||||
try {
|
||||
await mount(Parent, fixture);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe('Cannot find the definition of component "SomeMispelledWidget"');
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -909,4 +909,31 @@ describe("lifecycle hooks", () => {
|
||||
]);
|
||||
Object.freeze(steps);
|
||||
});
|
||||
|
||||
// TODO: rename (remove? seems covered by lifecycle semantics)
|
||||
test("sub widget (inside sub node): hooks are correctly called", async () => {
|
||||
let created = false;
|
||||
let mounted = false;
|
||||
|
||||
class Child extends Component {
|
||||
static template = xml`<div/>`;
|
||||
setup() {
|
||||
created = true;
|
||||
onMounted(() => { mounted = true; })
|
||||
}
|
||||
}
|
||||
class Parent extends Component {
|
||||
static template = xml`<Child t-if="state.flag"/>`;
|
||||
static components = { Child };
|
||||
state = useState({ flag: false });
|
||||
}
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(created).toBe(false);
|
||||
expect(mounted).toBe(false);
|
||||
|
||||
parent.state.flag = true;
|
||||
await nextTick();
|
||||
expect(mounted).toBe(true);
|
||||
expect(created).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -190,4 +190,24 @@ describe("t-component", () => {
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><div>1<button>Inc</button></div></div>");
|
||||
});
|
||||
|
||||
test.skip("t-component not on a <t> node", async () => {
|
||||
class Child extends Component {
|
||||
static template = xml`<span>1</span>`;
|
||||
}
|
||||
class Parent extends Component {
|
||||
static components = { Child };
|
||||
static template = xml`<div><div t-component="Child"/></div>`;
|
||||
}
|
||||
let error;
|
||||
try {
|
||||
await mount(Parent, fixture);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error).toBeDefined();
|
||||
expect(error.message).toBe(
|
||||
`Directive 't-component' can only be used on <t> nodes (used on a <div>)`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -197,4 +197,36 @@ describe("list of components", () => {
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(`<div><p>1</p><p>3</p></div>`);
|
||||
});
|
||||
|
||||
test("list of sub components inside other nodes", async () => {
|
||||
// this confuses the patching algorithm...
|
||||
class SubComponent extends Component {
|
||||
static template = xml`<span>asdf</span>`;
|
||||
}
|
||||
class Parent extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<div t-foreach="state.blips" t-as="blip" t-key="blip.id">
|
||||
<SubComponent />
|
||||
</div>
|
||||
</div>`;
|
||||
static components = { SubComponent };
|
||||
state = useState({
|
||||
blips: [
|
||||
{ a: "a", id: 1 },
|
||||
{ b: "b", id: 2 },
|
||||
{ c: "c", id: 4 },
|
||||
],
|
||||
});
|
||||
}
|
||||
const parent = await mount(Parent, fixture);
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><div><span>asdf</span></div><div><span>asdf</span></div><div><span>asdf</span></div></div>"
|
||||
);
|
||||
parent.state.blips.splice(0, 1);
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(
|
||||
"<div><div><span>asdf</span></div><div><span>asdf</span></div></div>"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user