mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[REF] component: large cleanup of concurrency branch
We remove here old comments, add some tests and documentation, and in general, make sure the state of the code is in a good shape part of #330
This commit is contained in:
@@ -12,7 +12,6 @@ exports[`animations t-transition combined with component 1`] = `
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
let def2;
|
||||
let templateId3 = \`__4__\`;
|
||||
let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false;
|
||||
let props3 = {};
|
||||
@@ -20,13 +19,17 @@ exports[`animations t-transition combined with component 1`] = `
|
||||
w3.destroy();
|
||||
w3 = false;
|
||||
}
|
||||
if (!w3) {
|
||||
if (w3) {
|
||||
w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey3 = \`Child\`;
|
||||
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child'];
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[templateId3] = w3.__owl__.id;
|
||||
def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
|
||||
w3.destroy();
|
||||
};
|
||||
@@ -35,12 +38,8 @@ exports[`animations t-transition combined with component 1`] = `
|
||||
def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
w3.__owl__.pvnode = pvnode;
|
||||
} else {
|
||||
def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
}
|
||||
sibling = w3.__owl__.currentFiber;
|
||||
sibling = w3.__owl__.currentFiber || sibling;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -58,7 +57,6 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
|
||||
var vn1 = h('div', p1, c1);
|
||||
if (context['state'].display) {
|
||||
//COMPONENT
|
||||
let def2;
|
||||
let templateId3 = \`__4__\`;
|
||||
let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false;
|
||||
let props3 = {};
|
||||
@@ -66,13 +64,17 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
|
||||
w3.destroy();
|
||||
w3 = false;
|
||||
}
|
||||
if (!w3) {
|
||||
if (w3) {
|
||||
w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey3 = \`Child\`;
|
||||
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child'];
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[templateId3] = w3.__owl__.id;
|
||||
def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
|
||||
w3.destroy();
|
||||
};
|
||||
@@ -81,12 +83,8 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
|
||||
def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
w3.__owl__.pvnode = pvnode;
|
||||
} else {
|
||||
def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
}
|
||||
sibling = w3.__owl__.currentFiber;
|
||||
sibling = w3.__owl__.currentFiber || sibling;
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,6 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
//COMPONENT
|
||||
let def2;
|
||||
let templateId3 = \`__4__\`;
|
||||
let w3 = templateId3 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId3]] : false;
|
||||
let props3 = {message:1};
|
||||
@@ -20,25 +19,25 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
|
||||
w3.destroy();
|
||||
w3 = false;
|
||||
}
|
||||
if (!w3) {
|
||||
if (w3) {
|
||||
utils.validateProps(w3.constructor, props3)
|
||||
w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey3 = \`Child\`;
|
||||
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child'];
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[templateId3] = w3.__owl__.id;
|
||||
def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () {if (w3.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
w3.__owl__.pvnode = pvnode;
|
||||
} else {
|
||||
utils.validateProps(w3.constructor, props3)
|
||||
def2 = def2 || w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
}
|
||||
sibling = w3.__owl__.currentFiber;
|
||||
sibling = w3.__owl__.currentFiber || sibling;
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -193,6 +193,23 @@ describe("basic widget properties", () => {
|
||||
expect(steps).toEqual(["__render", "mounted"]);
|
||||
});
|
||||
|
||||
test("render method wait until rendering is done", async () => {
|
||||
class TestW extends Component<any, any> {
|
||||
static template = xml`<div><t t-esc="state.drinks"/></div>`;
|
||||
state = { drinks: 1 };
|
||||
}
|
||||
const widget = new TestW(env);
|
||||
await widget.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>1</div>");
|
||||
|
||||
widget.state.drinks = 2;
|
||||
|
||||
const renderPromise = widget.render();
|
||||
expect(fixture.innerHTML).toBe("<div>1</div>");
|
||||
await renderPromise;
|
||||
expect(fixture.innerHTML).toBe("<div>2</div>");
|
||||
});
|
||||
|
||||
test("keeps a reference to env", async () => {
|
||||
const widget = new Widget(env);
|
||||
expect(widget.env).toBe(env);
|
||||
@@ -1553,6 +1570,44 @@ describe("composition", () => {
|
||||
expect(fixture.innerHTML).toBe("<div><div>world</div></div>");
|
||||
expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("sub components, loops, and shouldUpdate", async () => {
|
||||
class ChildWidget extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="props.val"/></span>`;
|
||||
shouldUpdate(nextProps) {
|
||||
if (nextProps.val === 12) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-foreach="state.records" t-as="record">
|
||||
<ChildWidget t-key="record.id" val="record.val"/>
|
||||
</t>
|
||||
</div>`;
|
||||
state = useState({
|
||||
records: [{ id: 1, val: 1 }, { id: 2, val: 2 }, { id: 3, val: 3 }]
|
||||
});
|
||||
static components = { ChildWidget };
|
||||
}
|
||||
const parent = new Parent(env);
|
||||
await parent.mount(fixture);
|
||||
expect(normalize(fixture.innerHTML)).toBe(
|
||||
"<div><span>1</span><span>2</span><span>3</span></div>"
|
||||
);
|
||||
|
||||
parent.state.records[0].val = 11;
|
||||
parent.state.records[1].val = 12;
|
||||
parent.state.records[2].val = 13;
|
||||
await nextTick();
|
||||
expect(normalize(fixture.innerHTML)).toBe(
|
||||
"<div><span>11</span><span>2</span><span>13</span></div>"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("props evaluation ", () => {
|
||||
@@ -2707,7 +2762,7 @@ describe("async rendering", () => {
|
||||
return defs[index++];
|
||||
}
|
||||
patched() {
|
||||
steps.push('patched');
|
||||
steps.push("patched");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2730,7 +2785,45 @@ describe("async rendering", () => {
|
||||
defs[1].resolve();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>3</span></div>");
|
||||
expect(steps).toEqual(['patched']);
|
||||
expect(steps).toEqual(["patched"]);
|
||||
});
|
||||
|
||||
test("update a sub-component twice in the same frame, 2", async () => {
|
||||
const steps: string[] = [];
|
||||
class ChildA extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="val()"/></span>`;
|
||||
patched() {
|
||||
steps.push("patched");
|
||||
}
|
||||
val() {
|
||||
steps.push("render");
|
||||
return this.props.val;
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`<div><ChildA val="state.valA"/></div>`;
|
||||
static components = { ChildA };
|
||||
state = useState({ valA: 1 });
|
||||
}
|
||||
const parent = new Parent(env);
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
parent.state.valA = 2;
|
||||
await nextMicroTick();
|
||||
expect(steps).toEqual(["render"]);
|
||||
await nextMicroTick();
|
||||
expect(steps).toEqual(["render", "render"]);
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
parent.state.valA = 3;
|
||||
await nextMicroTick();
|
||||
expect(steps).toEqual(["render", "render"]);
|
||||
await nextMicroTick();
|
||||
expect(steps).toEqual(["render", "render", "render"]);
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>3</span></div>");
|
||||
expect(steps).toEqual(["render", "render", "render", "patched"]);
|
||||
});
|
||||
|
||||
test("components in a node in a t-foreach ", async () => {
|
||||
|
||||
@@ -363,13 +363,12 @@ describe("default props", () => {
|
||||
}
|
||||
|
||||
class App extends Widget {
|
||||
static template = xml`<div><TestWidget/></div>`;
|
||||
static components = { TestWidget };
|
||||
static template = xml`<div><TestWidget/></div>`;
|
||||
static components = { TestWidget };
|
||||
}
|
||||
|
||||
const w = new App(env, {});
|
||||
await w.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe('<div><span>heyhey</span></div>')
|
||||
expect(fixture.innerHTML).toBe("<div><span>heyhey</span></div>");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
+8
-26
@@ -1,19 +1,15 @@
|
||||
import { Env } from "../src/component/component";
|
||||
import { Env, scheduler } from "../src/component/component";
|
||||
import { EvalContext, QWeb } from "../src/qweb/qweb";
|
||||
import { patch } from "../src/vdom";
|
||||
import "../src/qweb/base_directives";
|
||||
import "../src/qweb/extensions";
|
||||
import "../src/component/directive";
|
||||
|
||||
// modifies scheduler to make it easier to test components
|
||||
// let current;
|
||||
// scheduler.requestAnimationFrame = function(callback: FrameRequestCallback) {
|
||||
// if (current) {
|
||||
// throw new Error("should not schedule 2 callbacks!");
|
||||
// }
|
||||
// current = callback;
|
||||
// return 1;
|
||||
// };
|
||||
// modifies scheduler to make it faster to test components
|
||||
scheduler.requestAnimationFrame = function(callback: FrameRequestCallback) {
|
||||
setTimeout(callback, 1);
|
||||
return 1;
|
||||
};
|
||||
|
||||
// Some static cleanup
|
||||
let nextSlotId;
|
||||
@@ -22,7 +18,6 @@ let nextId;
|
||||
let TEMPLATES;
|
||||
|
||||
beforeEach(() => {
|
||||
// current = null;
|
||||
nextSlotId = QWeb.nextSlotId;
|
||||
slots = Object.assign({}, QWeb.slots);
|
||||
nextId = QWeb.nextId;
|
||||
@@ -30,7 +25,6 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// current = null;
|
||||
QWeb.nextSlotId = nextSlotId;
|
||||
QWeb.slots = slots;
|
||||
QWeb.nextId = nextId;
|
||||
@@ -41,22 +35,10 @@ afterEach(() => {
|
||||
export function nextMicroTick(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
// export async function nextTick(): Promise<void> {
|
||||
// await Promise.resolve();
|
||||
// let max = 1000;
|
||||
// while (current && max > 0) {
|
||||
// const cb = current;
|
||||
// console.warn('next');
|
||||
// current = null;
|
||||
// cb();
|
||||
// max--;
|
||||
// await Promise.resolve();
|
||||
// }
|
||||
// }
|
||||
|
||||
export async function nextTick(): Promise<void> {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(() => requestAnimationFrame(() => resolve()));
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(() => scheduler.requestAnimationFrame(() => resolve()));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ exports[`RouteComponent can render simple cases 1`] = `
|
||||
if (context['routeComponent']) {
|
||||
//COMPONENT
|
||||
let key3 = 'key' + context['env'].router.currentRouteName;
|
||||
let def1;
|
||||
let templateId2 = \`__4__\` + key3;
|
||||
let w2 = templateId2 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[templateId2]] : false;
|
||||
let vn5 = {};
|
||||
@@ -23,24 +22,24 @@ exports[`RouteComponent can render simple cases 1`] = `
|
||||
w2.destroy();
|
||||
w2 = false;
|
||||
}
|
||||
if (!w2) {
|
||||
if (w2) {
|
||||
w2.__updateProps(props2, extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
utils.defineProxy(vn5, pvnode);
|
||||
} else {
|
||||
let componentKey2 = \`routeComponent\`;
|
||||
let W2 = context.constructor.components[componentKey2] || QWeb.components[componentKey2]|| context['routeComponent'];
|
||||
if (!W2) {throw new Error('Cannot find the definition of component \\"' + componentKey2 + '\\"')}
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap[templateId2] = w2.__owl__.id;
|
||||
def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling);
|
||||
let def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = h('dummy', {key: templateId2, hook: {insert(vn) { let nvn=w2.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w2.destroy();}}});
|
||||
const fiber = w2.__owl__.currentFiber;
|
||||
def1.then(function () {if (w2.__owl__.isDestroyed) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
utils.defineProxy(vn5, pvnode);
|
||||
w2.__owl__.pvnode = pvnode;
|
||||
} else {
|
||||
def1 = def1 || w2.__updateProps(props2, extra.fiber, undefined, undefined, sibling);
|
||||
let pvnode = w2.__owl__.pvnode;
|
||||
utils.defineProxy(vn5, pvnode);
|
||||
}
|
||||
sibling = w2.__owl__.currentFiber;
|
||||
sibling = w2.__owl__.currentFiber || sibling;
|
||||
}
|
||||
return result;
|
||||
}"
|
||||
|
||||
Reference in New Issue
Block a user