[IMP] *: re-add a bunch of tests

This commit is contained in:
Samuel Degueldre
2021-10-21 10:13:08 +02:00
committed by Aaron Bohy
parent b6eb4d009e
commit ced5d0f69f
19 changed files with 1525 additions and 279 deletions
@@ -201,6 +201,41 @@ exports[`Portal portal could have dynamically no content 1`] = `
}"
`;
exports[`Portal portal destroys on crash 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 } = helpers;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
let d1 = ctx['props'].error&&this.will.crash;
return block1([d1]);
}
}"
`;
exports[`Portal portal destroys on crash 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 } = helpers;
let assign = Object.assign;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
const slot2 = ctx => (node, key) => {
return component(\`Child\`, {error: ctx['state'].error}, key + \`__3\`, node, ctx);
}
return function template(ctx, node, key = \\"\\") {
let b3 = assign(component(\`Portal\`, {target: '#outside'}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}});
return block1([], [b3]);
}
}"
`;
exports[`Portal portal with child and props 1`] = `
"function anonymous(bdom, helpers
) {
+170
View File
@@ -0,0 +1,170 @@
//import { AsyncRoot } from "../../src/misc/async_root";
import { useState, mount } from "../../src";
import { xml } from "../../src/tags";
import { makeDeferred, makeTestFixture, snapshotEverything, nextTick } from "../helpers";
import { Component } from "../../src/component/component";
let fixture: HTMLElement;
snapshotEverything();
beforeEach(() => {
fixture = makeTestFixture();
});
describe("Asyncroot", () => {
test.skip("delayed component with AsyncRoot component", async () => {
class Child extends Component {
static template = xml`<span><t t-esc="props.val"/></span>`;
}
class AsyncChild extends Child {
willUpdateProps() {
return def;
}
}
class Parent extends Component {
static template = xml`
<div>
<button t-on-click="updateApp">Update App State</button>
<div class="children">
<Child val="state.val"/>
<AsyncRoot>
<AsyncChild val="state.val"/>
</AsyncRoot>
</div>
</div>`;
static components = { Child, AsyncChild, /*AsyncRoot*/ };
state = useState({ val: 0 });
updateApp() {
this.state.val++;
}
}
await mount(Parent, fixture);
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0</span><span>0</span>");
// click on button to increment Parent counter
const def = makeDeferred();
fixture.querySelector("button")!.click();
await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1</span><span>0</span>");
def.resolve();
await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1</span><span>1</span>");
});
test.skip("fast component with AsyncRoot", async () => {
class Child extends Component {
static template = xml`<span><t t-esc="props.val"/></span>`;
}
class AsyncChild extends Child {
willUpdateProps() {
return def;
}
}
class Parent extends Component {
static template = xml`
<div>
<button t-on-click="updateApp">Update App State</button>
<div class="children">
<AsyncRoot>
<Child val="state.val"/>
</AsyncRoot>
<AsyncChild val="state.val"/>
</div>
</div>`;
static components = { Child, AsyncChild, /*AsyncRoot*/ };
state = useState({ val: 0 });
updateApp() {
this.state.val++;
}
}
await mount(Parent, fixture);
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0</span><span>0</span>");
// click on button to increment Parent counter
const def = makeDeferred();
fixture.querySelector("button")!.click();
await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1</span><span>0</span>");
def.resolve();
await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1</span><span>1</span>");
});
test.skip("asyncroot component: mixed re-renderings", async () => {
class Child extends Component {
static template = xml`
<span t-on-click="increment">
<t t-esc="state.val"/>/<t t-esc="props.val"/>
</span>`;
state = useState({ val: 0 });
increment() {
this.state.val++;
}
}
class AsyncChild extends Child {
willUpdateProps() {
return def;
}
}
class Parent extends Component {
static template = xml`
<div>
<button t-on-click="updateApp">Update App State</button>
<div class="children">
<Child val="state.val"/>
<AsyncRoot>
<AsyncChild val="state.val"/>
</AsyncRoot>
</div>
</div>`;
static components = { Child, AsyncChild, /*AsyncRoot*/ };
state = useState({ val: 0 });
updateApp() {
this.state.val++;
}
}
const parent = await mount(Parent, fixture);
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0/0</span><span>0/0</span>");
// click on button to increment Parent counter
const def = makeDeferred();
fixture.querySelector("button")!.click();
await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0/1</span><span>0/0</span>");
// click on each Child to increment their local counter
const children = (<HTMLElement>parent.el).querySelectorAll("span");
children[0]!.click();
await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1/1</span><span>0/0</span>");
children[1]!.click();
await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1/1</span><span>0/0</span>");
// finalize first re-rendering (coming from the props update)
def.resolve();
await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1/1</span><span>1/1</span>");
});
});
+70 -276
View File
@@ -379,283 +379,35 @@ describe("Portal", () => {
]);
});
// test.skip("portal destroys on crash", async () => {
// class Child extends Component {
// static template = xml`<span t-esc="props.error and this.will.crash" />`;
// state = {};
// }
// class Parent extends Component {
// static components = { Portal, Child };
// static template = xml`
// <div>
// <Portal target="'#outside'" >
// <Child error="state.error"/>
// </Portal>
// </div>`;
// state = { error: false };
// }
test("portal destroys on crash", async () => {
class Child extends Component {
static template = xml`<span t-esc="props.error and this.will.crash" />`;
state = {};
}
class Parent extends Component {
static components = { Portal, Child };
static template = xml`
<div>
<Portal target="'#outside'" >
<Child error="state.error"/>
</Portal>
</div>`;
state = { error: false };
}
addOutsideDiv(fixture);
const parent = await mount(Parent, fixture);
parent.state.error = true;
// const parent = new Parent();
// await parent.mount(fixture);
// parent.state.error = true;
// let error;
// try {
// await parent.render();
// } catch (e) {
// error = e;
// }
// expect(error).toBeDefined();
// const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g
// expect(error.message).toMatch(regexp);
// });
// test.skip("portal manual unmount", async () => {
// class Parent extends Component {
// static components = { Portal };
// static template = xml`
// <div>
// <Portal target="'#outside'">
// <span>gloria</span>
// </Portal>
// </div>`;
// }
// const parent = new Parent();
// await parent.mount(fixture);
// expect(outside.innerHTML).toBe("<span>gloria</span>");
// expect(parent.el!.innerHTML).toBe("<portal></portal>");
// parent.unmount();
// expect(outside.innerHTML).toBe("");
// expect(parent.el!.innerHTML).toBe("<portal><span>gloria</span></portal>");
// await parent.mount(fixture);
// expect(outside.innerHTML).toBe("<span>gloria</span>");
// expect(parent.el!.innerHTML).toBe("<portal></portal>");
// });
// test.skip("portal manual unmount with subcomponent", async () => {
// expect.assertions(9);
// class Child extends Component {
// static template = xml`<span>gloria</span>`;
// mounted() {
// expect(outside.contains(this.el)).toBeTruthy();
// }
// willUnmount() {
// expect(outside.contains(this.el)).toBeTruthy();
// }
// }
// class Parent extends Component {
// static components = { Portal, Child };
// static template = xml`
// <div>
// <Portal target="'#outside'">
// <Child />
// </Portal>
// </div>`;
// }
// const parent = new Parent();
// await parent.mount(fixture);
// expect(outside.innerHTML).toBe("<span>gloria</span>");
// expect(parent.el!.innerHTML).toBe("<portal></portal>");
// parent.unmount();
// expect(outside.innerHTML).toBe("");
// expect(parent.el!.innerHTML).toBe("<portal><span>gloria</span></portal>");
// await parent.mount(fixture);
// expect(outside.innerHTML).toBe("<span>gloria</span>");
// expect(parent.el!.innerHTML).toBe("<portal></portal>");
// });
// });
// describe("Portal: Events handling", () => {
// test.skip("events triggered on movable pure node are handled", async () => {
// class Parent extends Component {
// static components = { Portal };
// static template = xml`
// <div>
// <Portal target="'#outside'">
// <span id="trigger-me" t-on-custom="_onCustom" t-esc="state.val"/>
// </Portal>
// </div>`;
// state = useState({ val: "ab" });
// _onCustom() {
// this.state.val = "triggered";
// }
// }
// const parent = new Parent();
// await parent.mount(fixture);
// expect(outside.innerHTML).toBe(`<span id="trigger-me">ab</span>`);
// outside.querySelector("#trigger-me")!.dispatchEvent(new Event("custom"));
// await nextTick();
// expect(outside.innerHTML).toBe(`<span id="trigger-me">triggered</span>`);
// });
// test.skip("events triggered on movable owl components are redirected", async () => {
// let childInst: Component | null = null;
// class Child extends Component {
// static template = xml`
// <span t-on-custom="_onCustom" t-esc="props.val"/>`;
// constructor(parent, props) {
// super(parent, props);
// childInst = this;
// }
// _onCustom() {
// this.trigger("custom-portal");
// }
// }
// class Parent extends Component {
// static components = { Portal, Child };
// static template = xml`
// <div t-on-custom-portal="_onCustomPortal">
// <Portal target="'#outside'">
// <Child val="state.val"/>
// </Portal>
// </div>`;
// state = useState({ val: "ab" });
// _onCustomPortal() {
// this.state.val = "triggered";
// }
// }
// const parent = new Parent();
// await parent.mount(fixture);
// expect(outside.innerHTML).toBe(`<span>ab</span>`);
// childInst!.trigger("custom");
// await nextTick();
// expect(outside.innerHTML).toBe(`<span>triggered</span>`);
// });
// test.skip("events triggered on contained movable owl components are redirected", async () => {
// const steps: string[] = [];
// let childInst: Component | null = null;
// class Child extends Component {
// static template = xml`
// <span t-on-custom="_onCustom"/>`;
// constructor(parent, props) {
// super(parent, props);
// childInst = this;
// }
// _onCustom() {
// this.trigger("custom-portal");
// }
// }
// class Parent extends Component {
// static components = { Portal, Child };
// static template = xml`
// <div t-on-custom="_handled" t-on-custom-portal="_handled">
// <Portal target="'#outside'">
// <div>
// <Child/>
// </div>
// </Portal>
// </div>`;
// _handled(ev) {
// steps.push(ev.type);
// }
// }
// const parent = new Parent();
// await parent.mount(fixture);
// childInst!.trigger("custom");
// await nextTick();
// // This is expected because trigger is synchronous
// expect(steps).toMatchObject(["custom-portal", "custom"]);
// });
// test.skip("Dom events are not mapped", async () => {
// let childInst: Component | null = null;
// const steps: string[] = [];
// class Child extends Component {
// static template = xml`
// <button>child</button>`;
// constructor(parent, props) {
// super(parent, props);
// childInst = this;
// }
// }
// class Parent extends Component {
// static components = { Portal, Child };
// static template = xml`
// <div t-on-click="_handled">
// <Portal target="'#outside'">
// <Child />
// </Portal>
// </div>`;
// _handled(ev) {
// steps.push(ev.type as string);
// }
// }
// const bodyListener = (ev) => {
// steps.push(`body: ${ev.type}`);
// };
// document.body.addEventListener("click", bodyListener);
// const parent = new Parent();
// await parent.mount(fixture);
// childInst!.el!.click();
// expect(steps).toEqual(["body: click"]);
// document.body.removeEventListener("click", bodyListener);
// });
// test.skip("Nested portals event propagation", async () => {
// const outside2 = document.createElement("div");
// outside2.setAttribute("id", "outside2");
// fixture.appendChild(outside2);
// const steps: Array<string> = [];
// let childInst: Component | null = null;
// class Child2 extends Component {
// static template = xml`<div>child2</div>`;
// constructor(parent, props) {
// super(parent, props);
// childInst = this;
// }
// }
// class Child extends Component {
// static components = { Portal, Child2 };
// static template = xml`
// <Portal target="'#outside2'">
// <Child2 />
// </Portal>`;
// }
// class Parent extends Component {
// static components = { Portal, Child };
// static template = xml`
// <div t-on-custom='_handled'>
// <Portal target="'#outside'">
// <Child/>
// </Portal>
// </div>`;
// _handled(ev) {
// steps.push(`${ev.type} from ${ev.originalComponent.constructor.name}`);
// }
// }
// const parent = new Parent();
// await parent.mount(fixture);
// childInst!.trigger("custom");
// expect(steps).toEqual(["custom from Child2"]);
// });
let error;
try {
await parent.render();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g
expect(error.message).toMatch(regexp);
});
test("portal's parent's env is not polluted", async () => {
class Child extends Component {
@@ -750,3 +502,45 @@ describe("Portal: UI/UX", () => {
expect(document.activeElement === inputReRendered).toBeTruthy();
});
});
describe("Portal: Props validation", () => {
test.skip("target is mandatory", async () => {
class Parent extends Component {
static components = { Portal };
static template = xml`
<div>
<Portal>
<div>2</div>
</Portal>
</div>`;
}
let error;
try {
await mount(Parent, fixture);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe(`Missing props 'target' (component 'Portal')`);
});
test.skip("target is not list", async () => {
class Parent extends Component {
static components = { Portal };
static template = xml`
<div>
<Portal target="['body']">
<div>2</div>
</Portal>
</div>`;
}
let error;
try {
await mount(Parent, fixture);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe(`Invalid Prop 'target' in component 'Portal'`);
});
});