From dcefd26bee3b25c3189e66db6f62d5c9d663ee30 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A9ry=20Debongnie?=
Date: Sat, 27 Nov 2021 10:08:10 +0100
Subject: [PATCH] [REF] tests: improve useLogLifecycle and helpers
---
package.json | 1 +
tests/components/concurrency.test.ts | 1398 +++++++++++++-------------
tests/components/lifecycle.test.ts | 300 +++---
tests/components/t_component.test.ts | 35 +-
tests/components/t_foreach.test.ts | 10 +-
tests/helpers.ts | 79 +-
tests/reactivity.test.ts | 2 +-
7 files changed, 899 insertions(+), 926 deletions(-)
diff --git a/package.json b/package.json
index ac2822ed..c55b101a 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
"git-rev-sync": "^1.12.0",
"github-api": "^3.3.0",
"jest": "^27.1.0",
+ "jest-diff": "^27.3.1",
"jest-environment-jsdom": "^27.1.0",
"live-server": "^1.2.1",
"npm-run-all": "^4.1.5",
diff --git a/tests/components/concurrency.test.ts b/tests/components/concurrency.test.ts
index 4512ebae..d7f684dc 100644
--- a/tests/components/concurrency.test.ts
+++ b/tests/components/concurrency.test.ts
@@ -1,11 +1,14 @@
-import { App, Component, mount, onWillStart, onWillUpdateProps, useState } from "../../src";
-import { Fiber } from "../../src/component/fibers";
import {
+ App,
+ Component,
+ mount,
onMounted,
- onPatched,
- onWillPatch,
+ onWillStart,
onWillUnmount,
-} from "../../src/component/lifecycle_hooks";
+ onWillUpdateProps,
+ useState,
+} from "../../src";
+import { Fiber } from "../../src/component/fibers";
import { Scheduler } from "../../src/component/scheduler";
import { status } from "../../src/component/status";
import { xml } from "../../src/tags";
@@ -42,13 +45,12 @@ afterEach(() => {
describe("async rendering", () => {
test("destroying a widget before start is over", async () => {
- let steps: string[] = [];
let def = makeDeferred();
let w: any = null;
class W extends Component {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
expect(status(this)).toBe("new");
w = this;
onWillStart(() => def);
@@ -62,18 +64,17 @@ describe("async rendering", () => {
def.resolve();
await nextTick();
expect(status(w)).toBe("destroyed");
- expect(steps).toEqual(["W:setup", "W:willStart", "W:destroyed"]);
+ expect(["W:setup", "W:willStart", "W:destroyed"]).toBeLogged();
});
});
test("destroying/recreating a subwidget with different props (if start is not over)", async () => {
- let steps: string[] = [];
let def = makeDeferred();
let n = 0;
class Child extends Component {
static template = xml`child: `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
n++;
onWillStart(() => def);
}
@@ -87,12 +88,14 @@ test("destroying/recreating a subwidget with different props (if start is not ov
static components = { Child };
state = useState({ val: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const w = await mount(W, fixture);
+ expect(["W:setup", "W:willStart", "W:willRender", "W:rendered", "W:mounted"]).toBeLogged();
+
expect(n).toBe(0);
w.state.val = 2;
@@ -100,42 +103,35 @@ test("destroying/recreating a subwidget with different props (if start is not ov
await nextMicroTick();
expect(n).toBe(1);
+ expect(["W:willRender", "Child:setup", "Child:willStart", "W:rendered"]).toBeLogged();
+
w.state.val = 3;
await nextMicroTick();
await nextMicroTick();
expect(n).toBe(2);
- def.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("child:3
");
- expect(Object.values(w.__owl__.children).length).toBe(1);
- expect(steps).toEqual([
- "W:setup",
- "W:willStart",
- "W:willRender",
- "W:rendered",
- "W:mounted",
- "W:willRender",
- "Child:setup",
- "Child:willStart",
- "W:rendered",
+ expect([
"W:willRender",
"Child:destroyed",
"Child:setup",
"Child:willStart",
"W:rendered",
+ ]).toBeLogged();
+
+ def.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("child:3
");
+ expect(Object.values(w.__owl__.children).length).toBe(1);
+ expect([
"Child:willRender",
"Child:rendered",
"W:willPatch",
"Child:mounted",
"W:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("creating two async components, scenario 1", async () => {
- let steps: string[] = [];
-
let defA = makeDeferred();
let defB = makeDeferred();
let nbRenderings: number = 0;
@@ -144,7 +140,7 @@ test("creating two async components, scenario 1", async () => {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillStart(() => defA);
}
@@ -157,7 +153,7 @@ test("creating two async components, scenario 1", async () => {
class ChildB extends Component {
static template = xml`b `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillStart(() => defB);
}
}
@@ -170,36 +166,29 @@ test("creating two async components, scenario 1", async () => {
static components = { ChildA, ChildB };
state = useState({ flagA: false, flagB: false });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
- expect(fixture.innerHTML).toBe("");
- parent.state.flagA = true;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- parent.state.flagB = true;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- defB.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- expect(nbRenderings).toBe(0);
- defA.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("a b ");
- expect(nbRenderings).toBe(1);
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
"Parent:rendered",
"Parent:mounted",
- "Parent:willRender",
- "ChildA:setup",
- "ChildA:willStart",
- "Parent:rendered",
+ ]).toBeLogged();
+ expect(fixture.innerHTML).toBe("");
+
+ parent.state.flagA = true;
+ await nextTick();
+ expect(["Parent:willRender", "ChildA:setup", "ChildA:willStart", "Parent:rendered"]).toBeLogged();
+ expect(fixture.innerHTML).toBe("");
+
+ parent.state.flagB = true;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"Parent:willRender",
"ChildA:destroyed",
"ChildA:setup",
@@ -207,20 +196,29 @@ test("creating two async components, scenario 1", async () => {
"ChildB:setup",
"ChildB:willStart",
"Parent:rendered",
- "ChildB:willRender",
- "ChildB:rendered",
+ ]).toBeLogged();
+
+ defB.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect(nbRenderings).toBe(0);
+ expect(["ChildB:willRender", "ChildB:rendered"]).toBeLogged();
+
+ defA.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("a b ");
+ expect(nbRenderings).toBe(1);
+ expect([
"ChildA:willRender",
"ChildA:rendered",
"Parent:willPatch",
"ChildB:mounted",
"ChildA:mounted",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("creating two async components, scenario 2", async () => {
- let steps: string[] = [];
let defA = makeDeferred();
let defB = makeDeferred();
@@ -228,7 +226,7 @@ test("creating two async components, scenario 2", async () => {
static template = xml`a `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => defA);
}
}
@@ -236,7 +234,7 @@ test("creating two async components, scenario 2", async () => {
class ChildB extends Component {
static template = xml`b `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillStart(() => defB);
}
}
@@ -250,26 +248,11 @@ test("creating two async components, scenario 2", async () => {
static components = { ChildA, ChildB };
state = useState({ valA: 1, valB: 2, flagB: false });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
-
- expect(fixture.innerHTML).toBe("a1
");
- parent.state.valA = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("a1
");
- parent.state.flagB = true;
- await nextTick();
- expect(fixture.innerHTML).toBe("a1
");
- defB.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("a1
");
- defA.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("a2 b2
");
-
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -280,16 +263,34 @@ test("creating two async components, scenario 2", async () => {
"ChildA:rendered",
"ChildA:mounted",
"Parent:mounted",
- "Parent:willRender",
- "ChildA:willUpdateProps",
- "Parent:rendered",
+ ]).toBeLogged();
+ expect(fixture.innerHTML).toBe("a1
");
+
+ parent.state.valA = 2;
+ await nextTick();
+ expect(["Parent:willRender", "ChildA:willUpdateProps", "Parent:rendered"]).toBeLogged();
+ expect(fixture.innerHTML).toBe("a1
");
+
+ parent.state.flagB = true;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("a1
");
+ expect([
"Parent:willRender",
"ChildA:willUpdateProps",
"ChildB:setup",
"ChildB:willStart",
"Parent:rendered",
- "ChildB:willRender",
- "ChildB:rendered",
+ ]).toBeLogged();
+
+ defB.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("a1
");
+ expect(["ChildB:willRender", "ChildB:rendered"]).toBeLogged();
+
+ defA.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("a2 b2
");
+ expect([
"ChildA:willRender",
"ChildA:rendered",
"Parent:willPatch",
@@ -297,26 +298,24 @@ test("creating two async components, scenario 2", async () => {
"ChildB:mounted",
"ChildA:patched",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("creating two async components, scenario 3 (patching in the same frame)", async () => {
- let steps: string[] = [];
let defA = makeDeferred();
let defB = makeDeferred();
class ChildA extends Component {
static template = xml`a `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => defA);
}
}
class ChildB extends Component {
static template = xml`b `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillStart(() => defB);
}
}
@@ -330,25 +329,11 @@ test("creating two async components, scenario 3 (patching in the same frame)", a
static components = { ChildA, ChildB };
state = useState({ valA: 1, valB: 2, flagB: false });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
-
- expect(fixture.innerHTML).toBe("a1
");
- parent.state.valA = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("a1
");
- parent.state.flagB = true;
- await nextTick();
- expect(fixture.innerHTML).toBe("a1
");
- defB.resolve();
- expect(fixture.innerHTML).toBe("a1
");
- defA.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("a2 b2
");
-
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -359,14 +344,33 @@ test("creating two async components, scenario 3 (patching in the same frame)", a
"ChildA:rendered",
"ChildA:mounted",
"Parent:mounted",
- "Parent:willRender",
- "ChildA:willUpdateProps",
- "Parent:rendered",
+ ]).toBeLogged();
+ expect(fixture.innerHTML).toBe("a1
");
+
+ parent.state.valA = 2;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("a1
");
+ expect(["Parent:willRender", "ChildA:willUpdateProps", "Parent:rendered"]).toBeLogged();
+
+ parent.state.flagB = true;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("a1
");
+ expect([
"Parent:willRender",
"ChildA:willUpdateProps",
"ChildB:setup",
"ChildB:willStart",
"Parent:rendered",
+ ]).toBeLogged();
+
+ defB.resolve();
+ expect(fixture.innerHTML).toBe("a1
");
+ expect([]).toBeLogged();
+
+ defA.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("a2 b2
");
+ expect([
"ChildB:willRender",
"ChildB:rendered",
"ChildA:willRender",
@@ -376,19 +380,17 @@ test("creating two async components, scenario 3 (patching in the same frame)", a
"ChildB:mounted",
"ChildA:patched",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("update a sub-component twice in the same frame", async () => {
- const steps: string[] = [];
const defs = [makeDeferred(), makeDeferred()];
let index = 0;
class ChildA extends Component {
static template = xml` `;
setup() {
onWillUpdateProps(() => defs[index++]);
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -397,24 +399,12 @@ test("update a sub-component twice in the same frame", async () => {
static components = { ChildA };
state = useState({ valA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
-
expect(fixture.innerHTML).toBe("1
");
- parent.state.valA = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("1
");
- parent.state.valA = 3;
- await nextTick();
- expect(fixture.innerHTML).toBe("1
");
- defs[0].resolve();
- await Promise.resolve();
- defs[1].resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("3
");
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -425,33 +415,42 @@ test("update a sub-component twice in the same frame", async () => {
"ChildA:rendered",
"ChildA:mounted",
"Parent:mounted",
- "Parent:willRender",
- "ChildA:willUpdateProps",
- "Parent:rendered",
- "Parent:willRender",
- "ChildA:willUpdateProps",
- "Parent:rendered",
+ ]).toBeLogged();
+
+ parent.state.valA = 2;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("1
");
+ expect(["Parent:willRender", "ChildA:willUpdateProps", "Parent:rendered"]).toBeLogged();
+
+ parent.state.valA = 3;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("1
");
+ expect(["Parent:willRender", "ChildA:willUpdateProps", "Parent:rendered"]).toBeLogged();
+
+ defs[0].resolve();
+ await Promise.resolve();
+ defs[1].resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("3
");
+ expect([
"ChildA:willRender",
"ChildA:rendered",
"Parent:willPatch",
"ChildA:willPatch",
"ChildA:patched",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("update a sub-component twice in the same frame, 2", async () => {
- const steps: string[] = [];
class ChildA extends Component {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
val() {
- steps.push("render");
return this.props.val;
}
}
@@ -461,16 +460,11 @@ test("update a sub-component twice in the same frame, 2", async () => {
static components = { ChildA };
state = useState({ valA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
-
- expect(fixture.innerHTML).toBe("1
");
- parent.state.valA = 2;
- await nextMicroTick();
- await nextMicroTick();
- expect(steps.splice(0)).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -478,64 +472,48 @@ test("update a sub-component twice in the same frame, 2", async () => {
"ChildA:willStart",
"Parent:rendered",
"ChildA:willRender",
- "render",
"ChildA:rendered",
"ChildA:mounted",
"Parent:mounted",
- "Parent:willRender",
- "ChildA:willUpdateProps",
- "Parent:rendered",
- ]);
+ ]).toBeLogged();
+ expect(fixture.innerHTML).toBe("1
");
+
+ parent.state.valA = 2;
+ await nextMicroTick();
+ await nextMicroTick();
+ expect(["Parent:willRender", "ChildA:willUpdateProps", "Parent:rendered"]).toBeLogged();
await nextMicroTick();
// For an unknown reason, this test fails on windows without the next microtick. It works
// in linux and osx, but fails on at least this machine.
// I do not see anything harmful in waiting an extra tick. But it is annoying to not
// know what is different.
await nextMicroTick();
- expect(steps.splice(0)).toEqual(["ChildA:willRender", "render", "ChildA:rendered"]);
+ expect(["ChildA:willRender", "ChildA:rendered"]).toBeLogged();
expect(fixture.innerHTML).toBe("1
");
parent.state.valA = 3;
await nextMicroTick();
await nextMicroTick();
- expect(steps.splice(0)).toEqual([
- "Parent:willRender",
- "ChildA:willUpdateProps",
- "Parent:rendered",
- ]);
+ expect(["Parent:willRender", "ChildA:willUpdateProps", "Parent:rendered"]).toBeLogged();
+
await nextMicroTick();
// same as above
await nextMicroTick();
- expect(steps).toEqual(["ChildA:willRender", "render", "ChildA:rendered"]);
+ expect(["ChildA:willRender", "ChildA:rendered"]).toBeLogged();
expect(fixture.innerHTML).toBe("1
");
+
await nextTick();
expect(fixture.innerHTML).toBe("3
");
- expect(steps).toEqual([
- "ChildA:willRender",
- "render",
- "ChildA:rendered",
- "Parent:willPatch",
- "ChildA:willPatch",
- "ChildA:patched",
- "Parent:patched",
- ]);
- Object.freeze(steps);
+ expect(["Parent:willPatch", "ChildA:willPatch", "ChildA:patched", "Parent:patched"]).toBeLogged();
});
test("properly behave when destroyed/unmounted while rendering ", async () => {
- const steps: string[] = [];
const def = makeDeferred();
class SubChild extends Component {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
- onWillPatch(() => {
- throw new Error("Should not happen!");
- });
- onPatched(() => {
- throw new Error("Should not happen!");
- });
+ useLogLifecycle();
onWillUpdateProps(() => {
return def;
});
@@ -546,7 +524,7 @@ test("properly behave when destroyed/unmounted while rendering ", async () => {
static template = xml`
`;
static components = { SubChild };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -556,30 +534,13 @@ test("properly behave when destroyed/unmounted while rendering ", async () => {
static components = { Child };
state = useState({ flag: true, val: "Framboise Lindemans" });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("");
-
- // this change triggers a rendering of the parent. This rendering is delayed,
- // because child is now waiting for def to be resolved
- parent.state.val = "Framboise Girardin";
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- // with this, we remove child, and subchild, even though it is not finished
- // rendering from previous changes
- parent.state.flag = false;
- await nextTick();
- expect(fixture.innerHTML).toBe("
");
-
- // we now resolve def, so the child rendering is now complete.
- def.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("
");
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -595,12 +556,28 @@ test("properly behave when destroyed/unmounted while rendering ", async () => {
"SubChild:mounted",
"Child:mounted",
"Parent:mounted",
+ ]).toBeLogged();
+
+ // this change triggers a rendering of the parent. This rendering is delayed,
+ // because child is now waiting for def to be resolved
+ parent.state.val = "Framboise Girardin";
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"Parent:willRender",
"Child:willUpdateProps",
"Parent:rendered",
"Child:willRender",
"SubChild:willUpdateProps",
"Child:rendered",
+ ]).toBeLogged();
+
+ // with this, we remove child, and subchild, even though it is not finished
+ // rendering from previous changes
+ parent.state.flag = false;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("
");
+ expect([
"Parent:willRender",
"Parent:rendered",
"Parent:willPatch",
@@ -609,17 +586,20 @@ test("properly behave when destroyed/unmounted while rendering ", async () => {
"SubChild:destroyed",
"Child:destroyed",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
+
+ // we now resolve def, so the child rendering is now complete.
+ def.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("
");
+ expect([]).toBeLogged();
});
test("rendering component again in next microtick", async () => {
- const steps: string[] = [];
-
class Child extends Component {
static template = xml`Child
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -632,7 +612,7 @@ test("rendering component again in next microtick", async () => {
static components = { Child };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
async onClick() {
this.env.config.flag = true;
@@ -645,16 +625,18 @@ test("rendering component again in next microtick", async () => {
const env = { config: { flag: false } };
await new App(Parent).configure({ env }).mount(fixture);
expect(fixture.innerHTML).toBe("Click
");
- fixture.querySelector("button")!.click();
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
"Parent:rendered",
"Parent:mounted",
+ ]).toBeLogged();
+
+ fixture.querySelector("button")!.click();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"Parent:willRender",
"Child:setup",
"Child:willStart",
@@ -669,19 +651,17 @@ test("rendering component again in next microtick", async () => {
"Parent:willPatch",
"Child:mounted",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 1", async () => {
- const steps: string[] = [];
const def = makeDeferred();
let stateB: any = null;
class ComponentC extends Component {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => def);
}
someValue() {
@@ -697,7 +677,7 @@ test("concurrent renderings scenario 1", async () => {
setup() {
stateB = this.state;
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -706,31 +686,13 @@ test("concurrent renderings scenario 1", async () => {
static components = { ComponentB };
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const component = await mount(ComponentA, fixture);
-
expect(fixture.innerHTML).toBe("");
-
- stateB.fromB = "c";
- await nextTick();
-
- expect(fixture.innerHTML).toBe("");
-
- component.state.fromA = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- expect(ComponentC.prototype.someValue).toBeCalledTimes(1);
- def.resolve();
- await nextTick();
-
- expect(fixture.innerHTML).toBe("");
- expect(ComponentC.prototype.someValue).toBeCalledTimes(2);
-
- expect(steps).toEqual([
+ expect([
"ComponentA:setup",
"ComponentA:willStart",
"ComponentA:willRender",
@@ -746,15 +708,35 @@ test("concurrent renderings scenario 1", async () => {
"ComponentC:mounted",
"ComponentB:mounted",
"ComponentA:mounted",
+ ]).toBeLogged();
+
+ stateB.fromB = "c";
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentB:willRender",
"ComponentC:willUpdateProps",
"ComponentB:rendered",
+ ]).toBeLogged();
+
+ component.state.fromA = 2;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentA:willRender",
"ComponentB:willUpdateProps",
"ComponentA:rendered",
"ComponentB:willRender",
"ComponentC:willUpdateProps",
"ComponentB:rendered",
+ ]).toBeLogged();
+ expect(ComponentC.prototype.someValue).toBeCalledTimes(1);
+
+ def.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect(ComponentC.prototype.someValue).toBeCalledTimes(2);
+ expect([
"ComponentC:willRender",
"ComponentC:rendered",
"ComponentA:willPatch",
@@ -763,12 +745,10 @@ test("concurrent renderings scenario 1", async () => {
"ComponentC:patched",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 2", async () => {
- const steps: string[] = [];
// this test asserts that a rendering initiated before another one, and that
// ends after it, is re-mapped to that second rendering
const defs = [makeDeferred(), makeDeferred()];
@@ -778,7 +758,7 @@ test("concurrent renderings scenario 2", async () => {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => defs[index++]);
}
}
@@ -789,7 +769,7 @@ test("concurrent renderings scenario 2", async () => {
state = useState({ fromB: "b" });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
stateB = this.state;
}
}
@@ -799,30 +779,13 @@ test("concurrent renderings scenario 2", async () => {
static components = { ComponentB };
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const component = await mount(ComponentA, fixture);
-
expect(fixture.innerHTML).toBe("");
-
- component.state.fromA = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- stateB.fromB = "c";
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- defs[1].resolve(); // resolve rendering initiated in B
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- defs[0].resolve(); // resolve rendering initiated in A
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- expect(steps).toEqual([
+ expect([
"ComponentA:setup",
"ComponentA:willStart",
"ComponentA:willRender",
@@ -838,15 +801,33 @@ test("concurrent renderings scenario 2", async () => {
"ComponentC:mounted",
"ComponentB:mounted",
"ComponentA:mounted",
+ ]).toBeLogged();
+
+ component.state.fromA = 2;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentA:willRender",
"ComponentB:willUpdateProps",
"ComponentA:rendered",
"ComponentB:willRender",
"ComponentC:willUpdateProps",
"ComponentB:rendered",
+ ]).toBeLogged();
+
+ stateB.fromB = "c";
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentB:willRender",
"ComponentC:willUpdateProps",
"ComponentB:rendered",
+ ]).toBeLogged();
+
+ defs[1].resolve(); // resolve rendering initiated in B
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentC:willRender",
"ComponentC:rendered",
"ComponentA:willPatch",
@@ -855,12 +836,15 @@ test("concurrent renderings scenario 2", async () => {
"ComponentC:patched",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
+
+ defs[0].resolve(); // resolve rendering initiated in A
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([]).toBeLogged();
});
test("concurrent renderings scenario 2bis", async () => {
- const steps: string[] = [];
const defs = [makeDeferred(), makeDeferred()];
let index = 0;
let stateB: any = null;
@@ -868,7 +852,7 @@ test("concurrent renderings scenario 2bis", async () => {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => defs[index++]);
}
}
@@ -879,7 +863,7 @@ test("concurrent renderings scenario 2bis", async () => {
state = useState({ fromB: "b" });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
stateB = this.state;
}
}
@@ -890,30 +874,13 @@ test("concurrent renderings scenario 2bis", async () => {
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const component = await mount(ComponentA, fixture);
-
expect(fixture.innerHTML).toBe("");
-
- component.state.fromA = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- stateB.fromB = "c";
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- defs[0].resolve(); // resolve rendering initiated in A
- await nextTick();
- expect(fixture.innerHTML).toBe(""); // TODO: is this what we want?? 2b could be ok too
-
- defs[1].resolve(); // resolve rendering initiated in B
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- expect(steps).toEqual([
+ expect([
"ComponentA:setup",
"ComponentA:willStart",
"ComponentA:willRender",
@@ -929,15 +896,38 @@ test("concurrent renderings scenario 2bis", async () => {
"ComponentC:mounted",
"ComponentB:mounted",
"ComponentA:mounted",
+ ]).toBeLogged();
+
+ component.state.fromA = 2;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentA:willRender",
"ComponentB:willUpdateProps",
"ComponentA:rendered",
"ComponentB:willRender",
"ComponentC:willUpdateProps",
"ComponentB:rendered",
+ ]).toBeLogged();
+
+ stateB.fromB = "c";
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentB:willRender",
"ComponentC:willUpdateProps",
"ComponentB:rendered",
+ ]).toBeLogged();
+
+ defs[0].resolve(); // resolve rendering initiated in A
+ await nextTick();
+ expect(fixture.innerHTML).toBe(""); // TODO: is this what we want?? 2b could be ok too
+ expect([]).toBeLogged();
+
+ defs[1].resolve(); // resolve rendering initiated in B
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentC:willRender",
"ComponentC:rendered",
"ComponentA:willPatch",
@@ -946,12 +936,10 @@ test("concurrent renderings scenario 2bis", async () => {
"ComponentC:patched",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 3", async () => {
- const steps: string[] = [];
const defB = makeDeferred();
const defsD = [makeDeferred(), makeDeferred()];
let index = 0;
@@ -961,7 +949,7 @@ test("concurrent renderings scenario 3", async () => {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => defsD[index++]);
}
someValue() {
@@ -975,7 +963,7 @@ test("concurrent renderings scenario 3", async () => {
static components = { ComponentD };
state = useState({ fromC: "c" });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
stateC = this.state;
}
}
@@ -985,7 +973,7 @@ test("concurrent renderings scenario 3", async () => {
static components = { ComponentC };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => defB);
}
}
@@ -996,69 +984,76 @@ test("concurrent renderings scenario 3", async () => {
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const component = await mount(ComponentA, fixture);
-
expect(fixture.innerHTML).toBe("");
+ expect([
+ "ComponentA:setup",
+ "ComponentA:willStart",
+ "ComponentA:willRender",
+ "ComponentB:setup",
+ "ComponentB:willStart",
+ "ComponentA:rendered",
+ "ComponentB:willRender",
+ "ComponentC:setup",
+ "ComponentC:willStart",
+ "ComponentB:rendered",
+ "ComponentC:willRender",
+ "ComponentD:setup",
+ "ComponentD:willStart",
+ "ComponentC:rendered",
+ "ComponentD:willRender",
+ "ComponentD:rendered",
+ "ComponentD:mounted",
+ "ComponentC:mounted",
+ "ComponentB:mounted",
+ "ComponentA:mounted",
+ ]).toBeLogged();
component.state.fromA = 2;
await nextTick();
expect(fixture.innerHTML).toBe("");
+ expect([
+ "ComponentA:willRender",
+ "ComponentB:willUpdateProps",
+ "ComponentA:rendered",
+ ]).toBeLogged();
stateC.fromC = "d";
await nextTick();
expect(fixture.innerHTML).toBe("");
+ expect([
+ "ComponentC:willRender",
+ "ComponentD:willUpdateProps",
+ "ComponentC:rendered",
+ ]).toBeLogged();
defB.resolve(); // resolve rendering initiated in A (still blocked in D)
await nextTick();
expect(fixture.innerHTML).toBe("");
+ expect([
+ "ComponentB:willRender",
+ "ComponentC:willUpdateProps",
+ "ComponentB:rendered",
+ "ComponentC:willRender",
+ "ComponentD:willUpdateProps",
+ "ComponentC:rendered",
+ ]).toBeLogged();
defsD[0].resolve(); // resolve rendering initiated in C (should be ignored)
await nextTick();
expect(ComponentD.prototype.someValue).toBeCalledTimes(1);
expect(fixture.innerHTML).toBe("");
+ expect([]).toBeLogged();
defsD[1].resolve(); // completely resolve rendering initiated in A
await nextTick();
expect(fixture.innerHTML).toBe("");
expect(ComponentD.prototype.someValue).toBeCalledTimes(2);
-
- expect(steps).toEqual([
- "ComponentA:setup",
- "ComponentA:willStart",
- "ComponentA:willRender",
- "ComponentB:setup",
- "ComponentB:willStart",
- "ComponentA:rendered",
- "ComponentB:willRender",
- "ComponentC:setup",
- "ComponentC:willStart",
- "ComponentB:rendered",
- "ComponentC:willRender",
- "ComponentD:setup",
- "ComponentD:willStart",
- "ComponentC:rendered",
- "ComponentD:willRender",
- "ComponentD:rendered",
- "ComponentD:mounted",
- "ComponentC:mounted",
- "ComponentB:mounted",
- "ComponentA:mounted",
- "ComponentA:willRender",
- "ComponentB:willUpdateProps",
- "ComponentA:rendered",
- "ComponentC:willRender",
- "ComponentD:willUpdateProps",
- "ComponentC:rendered",
- "ComponentB:willRender",
- "ComponentC:willUpdateProps",
- "ComponentB:rendered",
- "ComponentC:willRender",
- "ComponentD:willUpdateProps",
- "ComponentC:rendered",
+ expect([
"ComponentD:willRender",
"ComponentD:rendered",
"ComponentA:willPatch",
@@ -1069,12 +1064,10 @@ test("concurrent renderings scenario 3", async () => {
"ComponentC:patched",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 4", async () => {
- const steps: string[] = [];
const defB = makeDeferred();
const defsD = [makeDeferred(), makeDeferred()];
let index = 0;
@@ -1084,7 +1077,7 @@ test("concurrent renderings scenario 4", async () => {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => defsD[index++]);
}
someValue() {
@@ -1098,7 +1091,7 @@ test("concurrent renderings scenario 4", async () => {
static components = { ComponentD };
state = useState({ fromC: "c" });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
stateC = this.state;
}
}
@@ -1108,7 +1101,7 @@ test("concurrent renderings scenario 4", async () => {
static components = { ComponentC };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => defB);
}
}
@@ -1119,37 +1112,13 @@ test("concurrent renderings scenario 4", async () => {
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const component = await mount(ComponentA, fixture);
-
expect(fixture.innerHTML).toBe("");
-
- component.state.fromA = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- stateC.fromC = "d";
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- defB.resolve(); // resolve rendering initiated in A (still blocked in D)
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- defsD[1].resolve(); // completely resolve rendering initiated in A
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- expect(ComponentD.prototype.someValue).toBeCalledTimes(2);
-
- defsD[0].resolve(); // resolve rendering initiated in C (should be ignored)
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- expect(ComponentD.prototype.someValue).toBeCalledTimes(2);
-
- expect(steps).toEqual([
+ expect([
"ComponentA:setup",
"ComponentA:willStart",
"ComponentA:willRender",
@@ -1170,18 +1139,43 @@ test("concurrent renderings scenario 4", async () => {
"ComponentC:mounted",
"ComponentB:mounted",
"ComponentA:mounted",
+ ]).toBeLogged();
+
+ component.state.fromA = 2;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentA:willRender",
"ComponentB:willUpdateProps",
"ComponentA:rendered",
+ ]).toBeLogged();
+
+ stateC.fromC = "d";
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentC:willRender",
"ComponentD:willUpdateProps",
"ComponentC:rendered",
+ ]).toBeLogged();
+
+ defB.resolve(); // resolve rendering initiated in A (still blocked in D)
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentB:willRender",
"ComponentC:willUpdateProps",
"ComponentB:rendered",
"ComponentC:willRender",
"ComponentD:willUpdateProps",
"ComponentC:rendered",
+ ]).toBeLogged();
+
+ defsD[1].resolve(); // completely resolve rendering initiated in A
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect(ComponentD.prototype.someValue).toBeCalledTimes(2);
+ expect([
"ComponentD:willRender",
"ComponentD:rendered",
"ComponentA:willPatch",
@@ -1192,12 +1186,16 @@ test("concurrent renderings scenario 4", async () => {
"ComponentC:patched",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
+
+ defsD[0].resolve(); // resolve rendering initiated in C (should be ignored)
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect(ComponentD.prototype.someValue).toBeCalledTimes(2);
+ expect([]).toBeLogged();
});
test("concurrent renderings scenario 5", async () => {
- const steps: string[] = [];
const defsB = [makeDeferred(), makeDeferred()];
let index = 0;
@@ -1205,7 +1203,7 @@ test("concurrent renderings scenario 5", async () => {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => defsB[index++]);
}
someValue() {
@@ -1219,60 +1217,64 @@ test("concurrent renderings scenario 5", async () => {
static template = xml`
`;
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const component = await mount(ComponentA, fixture);
-
expect(fixture.innerHTML).toBe("");
+ expect([
+ "ComponentA:setup",
+ "ComponentA:willStart",
+ "ComponentA:willRender",
+ "ComponentB:setup",
+ "ComponentB:willStart",
+ "ComponentA:rendered",
+ "ComponentB:willRender",
+ "ComponentB:rendered",
+ "ComponentB:mounted",
+ "ComponentA:mounted",
+ ]).toBeLogged();
component.state.fromA = 2;
await nextTick();
expect(fixture.innerHTML).toBe("");
+ expect([
+ "ComponentA:willRender",
+ "ComponentB:willUpdateProps",
+ "ComponentA:rendered",
+ ]).toBeLogged();
component.state.fromA = 3;
await nextTick();
expect(fixture.innerHTML).toBe("");
+ expect([
+ "ComponentA:willRender",
+ "ComponentB:willUpdateProps",
+ "ComponentA:rendered",
+ ]).toBeLogged();
defsB[0].resolve(); // resolve first re-rendering (should be ignored)
await nextTick();
expect(fixture.innerHTML).toBe("");
expect(ComponentB.prototype.someValue).toBeCalledTimes(1);
+ expect([]).toBeLogged();
defsB[1].resolve(); // resolve second re-rendering
await nextTick();
expect(fixture.innerHTML).toBe("");
expect(ComponentB.prototype.someValue).toBeCalledTimes(2);
- expect(steps).toEqual([
- "ComponentA:setup",
- "ComponentA:willStart",
- "ComponentA:willRender",
- "ComponentB:setup",
- "ComponentB:willStart",
- "ComponentA:rendered",
- "ComponentB:willRender",
- "ComponentB:rendered",
- "ComponentB:mounted",
- "ComponentA:mounted",
- "ComponentA:willRender",
- "ComponentB:willUpdateProps",
- "ComponentA:rendered",
- "ComponentA:willRender",
- "ComponentB:willUpdateProps",
- "ComponentA:rendered",
+ expect([
"ComponentB:willRender",
"ComponentB:rendered",
"ComponentA:willPatch",
"ComponentB:willPatch",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 6", async () => {
- const steps: string[] = [];
const defsB = [makeDeferred(), makeDeferred()];
let index = 0;
@@ -1280,7 +1282,7 @@ test("concurrent renderings scenario 6", async () => {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => defsB[index++]);
}
someValue() {
@@ -1295,32 +1297,13 @@ test("concurrent renderings scenario 6", async () => {
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const component = await mount(ComponentA, fixture);
-
expect(fixture.innerHTML).toBe("");
-
- component.state.fromA = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- component.state.fromA = 3;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- defsB[1].resolve(); // resolve second re-rendering
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- expect(ComponentB.prototype.someValue).toBeCalledTimes(2);
-
- defsB[0].resolve(); // resolve first re-rendering (should be ignored)
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- expect(ComponentB.prototype.someValue).toBeCalledTimes(2);
- expect(steps).toEqual([
+ expect([
"ComponentA:setup",
"ComponentA:willStart",
"ComponentA:willRender",
@@ -1331,31 +1314,53 @@ test("concurrent renderings scenario 6", async () => {
"ComponentB:rendered",
"ComponentB:mounted",
"ComponentA:mounted",
+ ]).toBeLogged();
+
+ component.state.fromA = 2;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentA:willRender",
"ComponentB:willUpdateProps",
"ComponentA:rendered",
+ ]).toBeLogged();
+
+ component.state.fromA = 3;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentA:willRender",
"ComponentB:willUpdateProps",
"ComponentA:rendered",
+ ]).toBeLogged();
+
+ defsB[1].resolve(); // resolve second re-rendering
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect(ComponentB.prototype.someValue).toBeCalledTimes(2);
+ expect([
"ComponentB:willRender",
"ComponentB:rendered",
"ComponentA:willPatch",
"ComponentB:willPatch",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
+
+ defsB[0].resolve(); // resolve first re-rendering (should be ignored)
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect(ComponentB.prototype.someValue).toBeCalledTimes(2);
+ expect([]).toBeLogged();
});
test("concurrent renderings scenario 7", async () => {
- const steps: string[] = [];
-
class ComponentB extends Component {
static template = xml`
`;
state = useState({ fromB: "b" });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => {
this.state.fromB = "c";
});
@@ -1371,20 +1376,14 @@ test("concurrent renderings scenario 7", async () => {
static template = xml`
`;
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const component = await mount(ComponentA, fixture);
-
expect(fixture.innerHTML).toBe("");
expect(ComponentB.prototype.someValue).toBeCalledTimes(1);
-
- component.state.fromA = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- expect(ComponentB.prototype.someValue).toBeCalledTimes(2);
- expect(steps).toEqual([
+ expect([
"ComponentA:setup",
"ComponentA:willStart",
"ComponentA:willRender",
@@ -1395,6 +1394,13 @@ test("concurrent renderings scenario 7", async () => {
"ComponentB:rendered",
"ComponentB:mounted",
"ComponentA:mounted",
+ ]).toBeLogged();
+
+ component.state.fromA = 2;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect(ComponentB.prototype.someValue).toBeCalledTimes(2);
+ expect([
"ComponentA:willRender",
"ComponentB:willUpdateProps",
"ComponentA:rendered",
@@ -1404,20 +1410,17 @@ test("concurrent renderings scenario 7", async () => {
"ComponentB:willPatch",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 8", async () => {
- const steps: string[] = [];
-
const def = makeDeferred();
let stateB: any = null;
class ComponentB extends Component {
static template = xml`
`;
state = useState({ fromB: "b" });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
stateB = this.state;
onWillUpdateProps(() => def);
}
@@ -1428,27 +1431,13 @@ test("concurrent renderings scenario 8", async () => {
static template = xml`
`;
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const component = await mount(ComponentA, fixture);
-
expect(fixture.innerHTML).toBe("");
-
- component.state.fromA = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- stateB.fromB = "c";
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- def.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- expect(steps).toEqual([
+ expect([
"ComponentA:setup",
"ComponentA:willStart",
"ComponentA:willRender",
@@ -1459,17 +1448,33 @@ test("concurrent renderings scenario 8", async () => {
"ComponentB:rendered",
"ComponentB:mounted",
"ComponentA:mounted",
+ ]).toBeLogged();
+
+ component.state.fromA = 2;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentA:willRender",
"ComponentB:willUpdateProps",
"ComponentA:rendered",
+ ]).toBeLogged();
+
+ stateB.fromB = "c";
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([]).toBeLogged();
+
+ def.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentB:willRender",
"ComponentB:rendered",
"ComponentA:willPatch",
"ComponentB:willPatch",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 9", async () => {
@@ -1485,14 +1490,13 @@ test("concurrent renderings scenario 9", async () => {
// C state is updated, producing a re-rendering of C and D
// this last re-rendering of C should be correctly re-mapped to the whole
// re-rendering
- const steps: string[] = [];
const def = makeDeferred();
let stateC: any = null;
class ComponentD extends Component {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -1503,7 +1507,7 @@ test("concurrent renderings scenario 9", async () => {
setup() {
stateC = this.state;
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
class ComponentB extends Component {
@@ -1511,7 +1515,7 @@ test("concurrent renderings scenario 9", async () => {
setup() {
onWillUpdateProps(() => def);
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
class ComponentA extends Component {
@@ -1524,27 +1528,13 @@ test("concurrent renderings scenario 9", async () => {
static components = { ComponentB, ComponentC };
state = useState({ fromA: "a1" });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const component = await mount(ComponentA, fixture);
-
expect(fixture.innerHTML).toBe("");
-
- component.state.fromA = "a2";
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- stateC.fromC = "b2";
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- def.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- expect(steps).toEqual([
+ expect([
"ComponentA:setup",
"ComponentA:willStart",
"ComponentA:willRender",
@@ -1565,6 +1555,12 @@ test("concurrent renderings scenario 9", async () => {
"ComponentC:mounted",
"ComponentB:mounted",
"ComponentA:mounted",
+ ]).toBeLogged();
+
+ component.state.fromA = "a2";
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentA:willRender",
"ComponentB:willUpdateProps",
"ComponentC:willUpdateProps",
@@ -1574,11 +1570,23 @@ test("concurrent renderings scenario 9", async () => {
"ComponentC:rendered",
"ComponentD:willRender",
"ComponentD:rendered",
+ ]).toBeLogged();
+
+ stateC.fromC = "b2";
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentC:willRender",
"ComponentD:willUpdateProps",
"ComponentC:rendered",
"ComponentD:willRender",
"ComponentD:rendered",
+ ]).toBeLogged();
+
+ def.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentB:willRender",
"ComponentB:rendered",
"ComponentA:willPatch",
@@ -1589,8 +1597,7 @@ test("concurrent renderings scenario 9", async () => {
"ComponentC:patched",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 10", async () => {
@@ -1609,7 +1616,6 @@ test("concurrent renderings scenario 10", async () => {
// the global re-rendering, but handlers waiting for the rendering promise to
// resolve might execute and we don't want them to crash/do anything
- const steps: string[] = [];
const defB = makeDeferred();
const defC = makeDeferred();
let stateB: any = null;
@@ -1617,7 +1623,7 @@ test("concurrent renderings scenario 10", async () => {
class ComponentC extends Component {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillStart(() => defC);
}
get value() {
@@ -1631,7 +1637,7 @@ test("concurrent renderings scenario 10", async () => {
state = useState({ hasChild: false });
static components = { ComponentC };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
stateB = this.state;
onWillUpdateProps(() => defB);
}
@@ -1643,26 +1649,13 @@ test("concurrent renderings scenario 10", async () => {
state = useState({ value: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const componentA = await mount(ComponentA, fixture);
expect(fixture.innerHTML).toBe("");
- stateB.hasChild = true;
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- componentA.state.value = 2;
- defC.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("");
-
- defB.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- expect(rendered).toBe(1);
- expect(steps).toEqual([
+ expect([
"ComponentA:setup",
"ComponentA:willStart",
"ComponentA:willRender",
@@ -1673,13 +1666,33 @@ test("concurrent renderings scenario 10", async () => {
"ComponentB:rendered",
"ComponentB:mounted",
"ComponentA:mounted",
+ ]).toBeLogged();
+
+ stateB.hasChild = true;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentB:willRender",
"ComponentC:setup",
"ComponentC:willStart",
"ComponentB:rendered",
+ ]).toBeLogged();
+
+ componentA.state.value = 2;
+ defC.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"ComponentA:willRender",
"ComponentB:willUpdateProps",
"ComponentA:rendered",
+ ]).toBeLogged();
+
+ defB.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect(rendered).toBe(1);
+ expect([
"ComponentB:willRender",
"ComponentC:destroyed",
"ComponentC:setup",
@@ -1692,8 +1705,7 @@ test("concurrent renderings scenario 10", async () => {
"ComponentC:mounted",
"ComponentB:patched",
"ComponentA:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 11", async () => {
@@ -1702,7 +1714,6 @@ test("concurrent renderings scenario 11", async () => {
// We check that in that case, the return value of the render method is a promise
// that is resolved when the component is completely rendered (so, properly
// remapped to the promise of the ambient rendering)
- const steps: string[] = [];
const def = makeDeferred();
let child: any = null;
class Child extends Component {
@@ -1710,7 +1721,7 @@ test("concurrent renderings scenario 11", async () => {
val = 3;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => {
child = this;
return def;
@@ -1723,22 +1734,12 @@ test("concurrent renderings scenario 11", async () => {
static components = { Child };
state = useState({ valA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("1|3
");
- parent.state.valA = 2;
-
- await nextTick();
- setTimeout(() => {
- def.resolve();
- }, 20);
- child.val = 5;
- await child.render();
- expect(fixture.innerHTML).toBe("2|5
");
-
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -1749,6 +1750,17 @@ test("concurrent renderings scenario 11", async () => {
"Child:rendered",
"Child:mounted",
"Parent:mounted",
+ ]).toBeLogged();
+
+ parent.state.valA = 2;
+ await nextTick();
+ setTimeout(() => {
+ def.resolve();
+ }, 20);
+ child.val = 5;
+ await child.render();
+ expect(fixture.innerHTML).toBe("2|5
");
+ expect([
"Parent:willRender",
"Child:willUpdateProps",
"Parent:rendered",
@@ -1758,8 +1770,7 @@ test("concurrent renderings scenario 11", async () => {
"Child:willPatch",
"Child:patched",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 12", async () => {
@@ -1769,13 +1780,12 @@ test("concurrent renderings scenario 12", async () => {
// to wait for its child (blocking) to be completed
// - twice in the next tick: it will twice reuse the same fiber (as it is
// rendered but not completed yet)
- const steps: string[] = [];
const def = makeDeferred();
class Child extends Component {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => def);
}
}
@@ -1787,7 +1797,7 @@ test("concurrent renderings scenario 12", async () => {
static components = { Child };
state = useState({ val: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
get val() {
@@ -1799,23 +1809,7 @@ test("concurrent renderings scenario 12", async () => {
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("1
");
expect(rendered).toBe(1);
-
- parent.state.val = 2;
- await nextTick();
- expect(fixture.innerHTML).toBe("1
");
- expect(rendered).toBe(2);
-
- parent.state.val = 3;
- parent.state.val = 4;
- await nextTick();
- expect(fixture.innerHTML).toBe("1
");
- expect(rendered).toBe(3);
-
- def.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("4
");
- expect(rendered).toBe(3);
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -1826,31 +1820,43 @@ test("concurrent renderings scenario 12", async () => {
"Child:rendered",
"Child:mounted",
"Parent:mounted",
- "Parent:willRender",
- "Child:willUpdateProps",
- "Parent:rendered",
- "Parent:willRender",
- "Child:willUpdateProps",
- "Parent:rendered",
+ ]).toBeLogged();
+
+ parent.state.val = 2;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("1
");
+ expect(rendered).toBe(2);
+ expect(["Parent:willRender", "Child:willUpdateProps", "Parent:rendered"]).toBeLogged();
+
+ parent.state.val = 3;
+ parent.state.val = 4;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("1
");
+ expect(rendered).toBe(3);
+ expect(["Parent:willRender", "Child:willUpdateProps", "Parent:rendered"]).toBeLogged();
+
+ def.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("4
");
+ expect(rendered).toBe(3);
+ expect([
"Child:willRender",
"Child:rendered",
"Parent:willPatch",
"Child:willPatch",
"Child:patched",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 13", async () => {
- const steps: string[] = [];
let lastChild: any = null;
class Child extends Component {
static template = xml` `;
state = useState({ val: 0 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onMounted(() => {
if (lastChild) {
lastChild.state.val = 0;
@@ -1870,21 +1876,13 @@ test("concurrent renderings scenario 13", async () => {
static components = { Child };
state = useState({ bool: false });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("0
");
-
- await nextTick(); // wait for changes triggered in mounted to be applied
- expect(fixture.innerHTML).toBe("1
");
-
- parent.state.bool = true;
- await nextTick(); // wait for this change to be applied
- await nextTick(); // wait for changes triggered in mounted to be applied
- expect(fixture.innerHTML).toBe("0 1
");
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -1897,8 +1895,15 @@ test("concurrent renderings scenario 13", async () => {
"Parent:mounted",
"Child:willRender",
"Child:rendered",
- "Child:willPatch",
- "Child:patched",
+ ]).toBeLogged();
+
+ await nextTick(); // wait for changes triggered in mounted to be applied
+ expect(fixture.innerHTML).toBe("1
");
+ expect(["Child:willPatch", "Child:patched"]).toBeLogged();
+
+ parent.state.bool = true;
+ await nextTick(); // wait for this change to be applied
+ expect([
"Parent:willRender",
"Child:willUpdateProps",
"Child:setup",
@@ -1917,16 +1922,14 @@ test("concurrent renderings scenario 13", async () => {
"Child:rendered",
"Child:willRender",
"Child:rendered",
- "Child:willPatch",
- "Child:patched",
- "Child:willPatch",
- "Child:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
+
+ await nextTick(); // wait for changes triggered in mounted to be applied
+ expect(fixture.innerHTML).toBe("0 1
");
+ expect(["Child:willPatch", "Child:patched", "Child:willPatch", "Child:patched"]).toBeLogged();
});
test("concurrent renderings scenario 14", async () => {
- const steps: string[] = [];
let b: B | undefined = undefined;
let c: C | undefined = undefined;
class C extends Component {
@@ -1939,7 +1942,7 @@ test("concurrent renderings scenario 14", async () => {
state = useState({ fromC: 3 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
c = this;
}
}
@@ -1947,7 +1950,7 @@ test("concurrent renderings scenario 14", async () => {
static template = xml`
`;
static components = { C };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
b = this;
}
state = useState({ fromB: 2 });
@@ -1959,35 +1962,12 @@ test("concurrent renderings scenario 14", async () => {
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const a = await mount(A, fixture);
expect(fixture.innerHTML).toBe("
1 2 3
");
-
- // trigger a re-rendering of the whole tree
- a.state.fromA += 10;
- // wait enough for the whole tree to be re-rendered, but not patched yet
- await nextMicroTick();
- await nextMicroTick();
- await nextMicroTick();
- await nextMicroTick();
- await nextMicroTick();
- expect(fixture.innerHTML).toBe("
1 2 3
");
-
- // trigger a re-rendering from C, which will remap its new fiber
- c!.state.fromC += 10;
- // trigger a re-rendering from B, which will remap its new fiber as well
- b!.state.fromB += 10;
-
- await nextTick();
- // at this point, all re-renderings should have been done correctly, and
- // the root fiber (A) counter should have been reset to 0, so the DOM should
- // have been patched with the updated version of each component
- expect(fixture.innerHTML).toBe(
- "
11 12 13
"
- );
- expect(steps).toEqual([
+ expect([
"A:setup",
"A:willStart",
"A:willRender",
@@ -2003,12 +1983,38 @@ test("concurrent renderings scenario 14", async () => {
"C:mounted",
"B:mounted",
"A:mounted",
+ ]).toBeLogged();
+
+ // trigger a re-rendering of the whole tree
+ a.state.fromA += 10;
+ // wait enough for the whole tree to be re-rendered, but not patched yet
+ await nextMicroTick();
+ await nextMicroTick();
+ await nextMicroTick();
+ await nextMicroTick();
+ await nextMicroTick();
+ expect(fixture.innerHTML).toBe("
1 2 3
");
+ expect([
"A:willRender",
"B:willUpdateProps",
"A:rendered",
"B:willRender",
"C:willUpdateProps",
"B:rendered",
+ ]).toBeLogged();
+
+ // trigger a re-rendering from C, which will remap its new fiber
+ c!.state.fromC += 10;
+ // trigger a re-rendering from B, which will remap its new fiber as well
+ b!.state.fromB += 10;
+ await nextTick();
+ // at this point, all re-renderings should have been done correctly, and
+ // the root fiber (A) counter should have been reset to 0, so the DOM should
+ // have been patched with the updated version of each component
+ expect(fixture.innerHTML).toBe(
+ "
11 12 13
"
+ );
+ expect([
"C:willRender",
"C:rendered",
"B:willRender",
@@ -2022,12 +2028,10 @@ test("concurrent renderings scenario 14", async () => {
"C:patched",
"B:patched",
"A:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("concurrent renderings scenario 15", async () => {
- const steps: string[] = [];
let b: B | undefined = undefined;
let c: C | undefined = undefined;
class C extends Component {
@@ -2040,7 +2044,7 @@ test("concurrent renderings scenario 15", async () => {
state = useState({ fromC: 3 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
c = this;
}
}
@@ -2048,7 +2052,7 @@ test("concurrent renderings scenario 15", async () => {
static template = xml`
`;
static components = { C };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
b = this;
}
state = useState({ fromB: 2 });
@@ -2058,12 +2062,29 @@ test("concurrent renderings scenario 15", async () => {
static components = { B };
state = useState({ fromA: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const app = new App(A);
const a = await app.mount(fixture);
expect(fixture.innerHTML).toBe("
1 2 3
");
+ expect([
+ "A:setup",
+ "A:willStart",
+ "A:willRender",
+ "B:setup",
+ "B:willStart",
+ "A:rendered",
+ "B:willRender",
+ "C:setup",
+ "C:willStart",
+ "B:rendered",
+ "C:willRender",
+ "C:rendered",
+ "C:mounted",
+ "B:mounted",
+ "A:mounted",
+ ]).toBeLogged();
// trigger a re-rendering of the whole tree
a.state.fromA += 10;
@@ -2074,6 +2095,14 @@ test("concurrent renderings scenario 15", async () => {
await nextMicroTick();
await nextMicroTick();
expect(fixture.innerHTML).toBe("
1 2 3
");
+ expect([
+ "A:willRender",
+ "B:willUpdateProps",
+ "A:rendered",
+ "B:willRender",
+ "C:willUpdateProps",
+ "B:rendered",
+ ]).toBeLogged();
// trigger a re-rendering from C, which will remap its new fiber
c!.state.fromC += 10;
@@ -2084,40 +2113,19 @@ test("concurrent renderings scenario 15", async () => {
// counter to 0)
app.scheduler.flush();
expect(fixture.innerHTML).toBe("
1 2 3
");
+ expect([]).toBeLogged();
// wait a bit and simulate another flush (we expect nothing to change as well)
await nextMicroTick();
app.scheduler.flush();
expect(fixture.innerHTML).toBe("
1 2 3
");
+ expect(["C:willRender", "C:rendered"]).toBeLogged();
await nextTick();
expect(fixture.innerHTML).toBe(
"
11 12 13
"
);
- expect(steps).toEqual([
- "A:setup",
- "A:willStart",
- "A:willRender",
- "B:setup",
- "B:willStart",
- "A:rendered",
- "B:willRender",
- "C:setup",
- "C:willStart",
- "B:rendered",
- "C:willRender",
- "C:rendered",
- "C:mounted",
- "B:mounted",
- "A:mounted",
- "A:willRender",
- "B:willUpdateProps",
- "A:rendered",
- "B:willRender",
- "C:willUpdateProps",
- "B:rendered",
- "C:willRender",
- "C:rendered",
+ expect([
"B:willRender",
"C:willUpdateProps",
"B:rendered",
@@ -2129,19 +2137,17 @@ test("concurrent renderings scenario 15", async () => {
"C:patched",
"B:patched",
"A:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test.skip("concurrent renderings scenario 16", async () => {
- const steps: string[] = [];
let b: B | undefined = undefined;
let c: C | undefined = undefined;
class D extends Component {
static template = xml``;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillStart(async () => {
await nextTick();
await nextTick();
@@ -2159,7 +2165,7 @@ test.skip("concurrent renderings scenario 16", async () => {
static components = { D };
state = { fromC: 3 }; // not reactive
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
c = this;
}
}
@@ -2167,7 +2173,7 @@ test.skip("concurrent renderings scenario 16", async () => {
static template = xml`
`;
static components = { C };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
b = this;
}
state = { fromB: 2 };
@@ -2178,11 +2184,28 @@ test.skip("concurrent renderings scenario 16", async () => {
state = { fromA: 1 };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const a = await mount(A, fixture);
expect(fixture.innerHTML).toBe("
1 2 3
");
+ expect([
+ "A:setup",
+ "A:willStart",
+ "A:willRender",
+ "B:setup",
+ "B:willStart",
+ "A:rendered",
+ "B:willRender",
+ "C:setup",
+ "C:willStart",
+ "B:rendered",
+ "C:willRender",
+ "C:rendered",
+ "C:mounted",
+ "B:mounted",
+ "A:mounted",
+ ]).toBeLogged();
// trigger a re-rendering of the whole tree
a.state.fromA += 10;
@@ -2210,22 +2233,7 @@ test.skip("concurrent renderings scenario 16", async () => {
expect(fixture.innerHTML).toBe(
"
11 12 13
"
);
- expect(steps).toEqual([
- "A:setup",
- "A:willStart",
- "A:willRender",
- "B:setup",
- "B:willStart",
- "A:rendered",
- "B:willRender",
- "C:setup",
- "C:willStart",
- "B:rendered",
- "C:willRender",
- "C:rendered",
- "C:mounted",
- "B:mounted",
- "A:mounted",
+ expect([
"A:willRender",
"B:willUpdateProps",
"A:rendered",
@@ -2253,35 +2261,10 @@ test.skip("concurrent renderings scenario 16", async () => {
"C:patched",
"B:patched",
"A:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
-// test.skip("concurrent renderings scenario 17", async () => {
-// class Parent extends Component {
-// static template = xml` `;
-// state = useState({ value: 1 });
-// }
-
-// const parent = await mount(Parent, fixture);
-// expect(fixture.innerHTML).toBe("1 ");
-
-// parent.state.value = 2;
-// parent.__owl__.fiber!.cancel();
-
-// parent.state.value = 3; // update value directly
-// await nextTick();
-// expect(fixture.innerHTML).toBe("3 ");
-
-// parent.state.value = 4; // update value after a tick
-// await nextTick();
-// expect(fixture.innerHTML).toBe("4 ");
-// });
-
-// TODO: unskip when t-key is reimplemented properly
test("calling render in destroy", async () => {
- const steps: any[] = [];
-
let a: any = null;
let c: any = null;
@@ -2298,9 +2281,9 @@ test("calling render in destroy", async () => {
static components = { C };
setup() {
+ useLogLifecycle();
c = this;
onMounted(() => {
- steps.push("B:mounted");
if (flag) {
this.render();
} else {
@@ -2309,7 +2292,6 @@ test("calling render in destroy", async () => {
});
onWillUnmount(() => {
- steps.push("B:willUnmount");
c.render();
});
}
@@ -2329,19 +2311,30 @@ test("calling render in destroy", async () => {
const app = new App(A);
await app.mount(fixture);
expect(fixture.innerHTML).toBe("a
");
+ expect(["B:setup", "B:willStart", "B:willRender", "B:rendered", "B:mounted"]).toBeLogged();
a.state = "A";
a.key = 2;
await a.render();
// this nextTick is critical, otherwise jest may silently swallow errors
await nextTick();
-
- expect(steps).toStrictEqual(["B:mounted", "B:willUnmount", "B:mounted"]);
+ expect([
+ "B:setup",
+ "B:willStart",
+ "B:willRender",
+ "B:rendered",
+ "B:willUnmount",
+ "B:destroyed",
+ "B:mounted",
+ "B:willRender",
+ "B:rendered",
+ "B:willPatch",
+ "B:patched",
+ ]).toBeLogged();
expect(fixture.innerHTML).toBe("A
");
});
test("change state and call manually render: no unnecessary rendering", async () => {
- const steps: string[] = [];
let numberOfRender = 0;
class Test extends Component {
@@ -2349,7 +2342,7 @@ test("change state and call manually render: no unnecessary rendering", async ()
state = useState({ val: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
get value() {
numberOfRender++;
@@ -2358,6 +2351,13 @@ test("change state and call manually render: no unnecessary rendering", async ()
}
const test = await mount(Test, fixture);
+ expect([
+ "Test:setup",
+ "Test:willStart",
+ "Test:willRender",
+ "Test:rendered",
+ "Test:mounted",
+ ]).toBeLogged();
expect(fixture.innerHTML).toBe("1
");
expect(numberOfRender).toBe(1);
@@ -2365,29 +2365,17 @@ test("change state and call manually render: no unnecessary rendering", async ()
await test.render();
expect(fixture.innerHTML).toBe("2
");
expect(numberOfRender).toBe(2);
- expect(steps).toEqual([
- "Test:setup",
- "Test:willStart",
- "Test:willRender",
- "Test:rendered",
- "Test:mounted",
- "Test:willRender",
- "Test:rendered",
- "Test:willPatch",
- "Test:patched",
- ]);
- Object.freeze(steps);
+ expect(["Test:willRender", "Test:rendered", "Test:willPatch", "Test:patched"]).toBeLogged();
});
test("changing state before first render does not trigger a render", async () => {
- const steps: string[] = [];
let renders = 0;
class TestW extends Component {
static template = xml`
`;
state = useState({ drinks: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
this.state.drinks++;
onWillStart(() => {
this.state.drinks++;
@@ -2399,29 +2387,28 @@ test("changing state before first render does not trigger a render", async () =>
}
}
await mount(TestW, fixture);
-
- await nextTick();
- expect(renders).toBe(1);
- expect(fixture.innerHTML).toBe("3
");
- expect(steps).toEqual([
+ expect([
"TestW:setup",
"TestW:willStart",
"TestW:willRender",
"TestW:rendered",
"TestW:mounted",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
+
+ await nextTick();
+ expect(renders).toBe(1);
+ expect(fixture.innerHTML).toBe("3
");
+ expect([]).toBeLogged();
});
test("changing state before first render does not trigger a render (with parent)", async () => {
- const steps: string[] = [];
let renders = 0;
class TestW extends Component {
static template = xml`
`;
state = useState({ drinks: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
this.state.drinks++;
onWillStart(() => {
this.state.drinks++;
@@ -2437,27 +2424,26 @@ test("changing state before first render does not trigger a render (with parent)
static components = { TestW };
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
state = useState({ flag: false });
}
const parent = await mount(Parent, fixture);
-
expect(fixture.innerHTML).toBe("
");
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
"Parent:rendered",
"Parent:mounted",
- ]);
- steps.splice(0);
+ ]).toBeLogged();
+
parent.state.flag = true;
await nextTick();
expect(fixture.innerHTML).toBe("");
expect(renders).toBe(1);
- expect(steps).toEqual([
+ expect([
"Parent:willRender",
"TestW:setup",
"TestW:willStart",
@@ -2467,8 +2453,7 @@ test("changing state before first render does not trigger a render (with parent)
"Parent:willPatch",
"TestW:mounted",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("render method wait until rendering is done", async () => {
@@ -2489,12 +2474,11 @@ test("render method wait until rendering is done", async () => {
test("two renderings initiated between willPatch and patched", async () => {
let parent: any = null;
- const steps: string[] = [];
class Panel extends Component {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onMounted(() => parent.render());
onWillUnmount(() => parent.render());
}
@@ -2506,15 +2490,14 @@ test("two renderings initiated between willPatch and patched", async () => {
static components = { Panel };
state = useState({ panel: "Panel1", flag: true });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
parent = this;
}
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("");
-
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -2525,14 +2508,12 @@ test("two renderings initiated between willPatch and patched", async () => {
"Panel:rendered",
"Panel:mounted",
"Parent:mounted",
- ]);
- steps.length = 0;
+ ]).toBeLogged();
parent.state.panel = "Panel2";
await nextTick();
expect(fixture.innerHTML).toBe("");
-
- expect(steps).toEqual([
+ expect([
"Parent:willRender",
"Panel:setup",
"Panel:willStart",
@@ -2544,33 +2525,29 @@ test("two renderings initiated between willPatch and patched", async () => {
"Panel:destroyed",
"Panel:mounted",
"Parent:patched",
- ]);
- steps.length = 0;
+ ]).toBeLogged();
parent.state.flag = false;
await nextTick();
expect(fixture.innerHTML).toBe("
");
-
- expect(steps).toEqual([
+ expect([
"Parent:willRender",
"Parent:rendered",
"Parent:willPatch",
"Panel:willUnmount",
"Panel:destroyed",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("parent and child rendered at exact same time", async () => {
let child: any = null;
- let steps: any[] = [];
class Child extends Component {
static template = xml` `;
setup() {
child = this;
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -2579,20 +2556,13 @@ test("parent and child rendered at exact same time", async () => {
static components = { Child };
state = { value: 0 };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
-
expect(fixture.innerHTML).toBe("0");
-
- parent.state.value = 1;
- parent.render();
- child.render();
- await nextTick();
- expect(fixture.innerHTML).toBe("1");
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -2603,6 +2573,14 @@ test("parent and child rendered at exact same time", async () => {
"Child:rendered",
"Child:mounted",
"Parent:mounted",
+ ]).toBeLogged();
+
+ parent.state.value = 1;
+ parent.render();
+ child.render();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("1");
+ expect([
"Parent:willRender",
"Child:willUpdateProps",
"Parent:rendered",
@@ -2612,7 +2590,7 @@ test("parent and child rendered at exact same time", async () => {
"Child:willPatch",
"Child:patched",
"Parent:patched",
- ]);
+ ]).toBeLogged();
});
// test.skip("components with shouldUpdate=false", async () => {
diff --git a/tests/components/lifecycle.test.ts b/tests/components/lifecycle.test.ts
index 4b877991..376c488c 100644
--- a/tests/components/lifecycle.test.ts
+++ b/tests/components/lifecycle.test.ts
@@ -346,12 +346,10 @@ describe("lifecycle hooks", () => {
});
test("components are unmounted and destroyed if no longer in DOM, even after updateprops", async () => {
- let steps: string[] = [];
-
class Child extends Component {
static template = xml` `;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -363,7 +361,7 @@ describe("lifecycle hooks", () => {
`;
static components = { Child };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
state = useState({ n: 0, flag: true });
increment() {
@@ -376,13 +374,7 @@ describe("lifecycle hooks", () => {
const parent = await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("0
");
- parent.increment();
- await nextTick();
- expect(fixture.innerHTML).toBe("1
");
- parent.toggleSubWidget();
- await nextTick();
- expect(fixture.innerHTML).toBe("");
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -393,6 +385,12 @@ describe("lifecycle hooks", () => {
"Child:rendered",
"Child:mounted",
"Parent:mounted",
+ ]).toBeLogged();
+
+ parent.increment();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("1
");
+ expect([
"Parent:willRender",
"Child:willUpdateProps",
"Parent:rendered",
@@ -402,32 +400,26 @@ describe("lifecycle hooks", () => {
"Child:willPatch",
"Child:patched",
"Parent:patched",
+ ]).toBeLogged();
+
+ parent.toggleSubWidget();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("");
+ expect([
"Parent:willRender",
"Parent:rendered",
"Parent:willPatch",
"Child:willUnmount",
"Child:destroyed",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("hooks are called in proper order in widget creation/destruction", async () => {
- let steps: string[] = [];
-
class Child extends Component {
static template = xml`
`;
setup() {
- steps.push("c init");
- onWillStart(() => {
- steps.push("c willstart");
- });
- onMounted(() => {
- steps.push("c mounted");
- });
- onWillUnmount(() => {
- steps.push("c willunmount");
- });
+ useLogLifecycle();
}
}
@@ -435,33 +427,32 @@ describe("lifecycle hooks", () => {
static template = xml`
`;
static components = { Child };
setup() {
- steps.push("p init");
- onWillStart(() => {
- steps.push("p willstart");
- });
- onMounted(() => {
- steps.push("p mounted");
- });
- onWillUnmount(() => {
- steps.push("p willunmount");
- });
+ useLogLifecycle();
}
}
const app = new App(Parent);
await app.mount(fixture);
+ expect([
+ "Parent:setup",
+ "Parent:willStart",
+ "Parent:willRender",
+ "Child:setup",
+ "Child:willStart",
+ "Parent:rendered",
+ "Child:willRender",
+ "Child:rendered",
+ "Child:mounted",
+ "Parent:mounted",
+ ]).toBeLogged();
+
app.destroy();
- expect(steps).toEqual([
- "p init",
- "p willstart",
- "c init",
- "c willstart",
- "c mounted",
- "p mounted",
- "p willunmount",
- "c willunmount",
- ]);
- Object.freeze(steps);
+ expect([
+ "Parent:willUnmount",
+ "Child:willUnmount",
+ "Child:destroyed",
+ "Parent:destroyed",
+ ]).toBeLogged();
});
test("willUpdateProps hook is called", async () => {
@@ -543,12 +534,10 @@ describe("lifecycle hooks", () => {
});
test("lifecycle semantics", async () => {
- let steps: string[] = [];
-
class Child extends Component {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
class Parent extends Component {
@@ -556,14 +545,13 @@ describe("lifecycle hooks", () => {
static components = { Child };
state = useState({ a: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const app = new App(Parent);
await app.mount(fixture);
-
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -574,33 +562,29 @@ describe("lifecycle hooks", () => {
"Child:rendered",
"Child:mounted",
"Parent:mounted",
- ]);
+ ]).toBeLogged();
- steps.splice(0);
app.destroy();
- expect(steps).toEqual([
+ expect([
"Parent:willUnmount",
"Child:willUnmount",
"Child:destroyed",
"Parent:destroyed",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("lifecycle semantics, part 2", async () => {
- let steps: string[] = [];
-
class GrandChild extends Component {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
class Child extends Component {
static template = xml` `;
static components = { GrandChild };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -609,26 +593,23 @@ describe("lifecycle hooks", () => {
static components = { Child };
state = useState({ hasChild: false });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const app = new App(Parent);
const parent = await app.mount(fixture);
-
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
"Parent:rendered",
"Parent:mounted",
- ]);
-
- steps.splice(0);
+ ]).toBeLogged();
parent.state.hasChild = true;
await nextTick();
- expect(steps).toEqual([
+ expect([
"Parent:willRender",
"Child:setup",
"Child:willStart",
@@ -643,36 +624,31 @@ describe("lifecycle hooks", () => {
"GrandChild:mounted",
"Child:mounted",
"Parent:patched",
- ]);
-
- steps.splice(0);
+ ]).toBeLogged();
app.destroy();
- expect(steps).toEqual([
+ expect([
"Parent:willUnmount",
"Child:willUnmount",
"GrandChild:willUnmount",
"GrandChild:destroyed",
"Child:destroyed",
"Parent:destroyed",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("lifecycle semantics, part 3", async () => {
- let steps: string[] = [];
-
class GrandChild extends Component {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
class Child extends Component {
static template = xml` `;
static components = { GrandChild };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -681,41 +657,34 @@ describe("lifecycle hooks", () => {
static components = { Child };
state = useState({ hasChild: false });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const app = new App(Parent);
const parent = await app.mount(fixture);
-
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
"Parent:rendered",
"Parent:mounted",
- ]);
-
- steps.splice(0);
+ ]).toBeLogged();
parent.state.hasChild = true;
-
// immediately destroy everything
app.destroy();
await nextTick();
- expect(steps).toEqual(["Parent:willUnmount", "Parent:destroyed"]);
- Object.freeze(steps);
+ expect(["Parent:willUnmount", "Parent:destroyed"]).toBeLogged();
});
test("lifecycle semantics, part 4", async () => {
let def = makeDeferred();
- let steps: string[] = [];
-
class GrandChild extends Component {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillStart(() => def);
}
}
@@ -723,7 +692,7 @@ describe("lifecycle hooks", () => {
static template = xml` `;
static components = { GrandChild };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -732,27 +701,24 @@ describe("lifecycle hooks", () => {
static components = { Child };
state = useState({ hasChild: false });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const app = new App(Parent);
const parent = await app.mount(fixture);
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
"Parent:rendered",
"Parent:mounted",
- ]);
-
- steps.splice(0);
+ ]).toBeLogged();
parent.state.hasChild = true;
-
await nextTick();
- expect(steps).toEqual([
+ expect([
"Parent:willRender",
"Child:setup",
"Child:willStart",
@@ -761,27 +727,22 @@ describe("lifecycle hooks", () => {
"GrandChild:setup",
"GrandChild:willStart",
"Child:rendered",
- ]);
-
- steps.splice(0);
+ ]).toBeLogged();
app.destroy();
- expect(steps).toEqual([
+ expect([
"Parent:willUnmount",
"GrandChild:destroyed",
"Child:destroyed",
"Parent:destroyed",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("lifecycle semantics, part 5", async () => {
- let steps: string[] = [];
-
class Child extends Component {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -790,13 +751,12 @@ describe("lifecycle hooks", () => {
static components = { Child };
state = useState({ hasChild: true });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
-
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -807,31 +767,25 @@ describe("lifecycle hooks", () => {
"Child:rendered",
"Child:mounted",
"Parent:mounted",
- ]);
-
- steps.splice(0);
+ ]).toBeLogged();
parent.state.hasChild = false;
-
await nextTick();
- expect(steps).toEqual([
+ expect([
"Parent:willRender",
"Parent:rendered",
"Parent:willPatch",
"Child:willUnmount",
"Child:destroyed",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("lifecycle semantics, part 6", async () => {
- let steps: string[] = [];
-
class Child extends Component {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -840,13 +794,12 @@ describe("lifecycle hooks", () => {
static components = { Child };
state = useState({ value: 1 });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
-
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -857,14 +810,11 @@ describe("lifecycle hooks", () => {
"Child:rendered",
"Child:mounted",
"Parent:mounted",
- ]);
-
- steps.splice(0);
+ ]).toBeLogged();
parent.state.value = 2;
-
await nextTick();
- expect(steps).toEqual([
+ expect([
"Parent:willRender",
"Child:willUpdateProps",
"Parent:rendered",
@@ -874,12 +824,10 @@ describe("lifecycle hooks", () => {
"Child:willPatch",
"Child:patched",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
test("onWillRender", async () => {
- let steps: string[] = [];
const def = makeDeferred();
class Child extends Component {
@@ -887,13 +835,12 @@ describe("lifecycle hooks", () => {
state = useState({ value: 1 });
visibleState = this.state.value;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
onWillUpdateProps(() => def);
onWillRender(() => (this.visibleState = this.state.value));
}
increment() {
this.state.value++;
- steps.push(`inc:${this.visibleState}`);
}
}
@@ -902,30 +849,13 @@ describe("lifecycle hooks", () => {
`;
static components = { Child };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
-
expect(fixture.innerHTML).toBe("1 ");
-
- parent.render(); // to block child render
- await nextTick();
-
- fixture.querySelector("button")!.click();
-
- await nextTick();
- fixture.querySelector("button")!.click();
-
- await nextTick();
-
- expect(fixture.innerHTML).toBe("1 ");
-
- def.resolve();
- await nextTick();
- expect(fixture.innerHTML).toBe("3 ");
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -936,19 +866,32 @@ describe("lifecycle hooks", () => {
"Child:rendered",
"Child:mounted",
"Parent:mounted",
- "Parent:willRender",
- "Child:willUpdateProps",
- "Parent:rendered",
- "inc:1",
- "inc:1",
+ ]).toBeLogged();
+
+ parent.render(); // to block child render
+ await nextTick();
+ expect(["Parent:willRender", "Child:willUpdateProps", "Parent:rendered"]).toBeLogged();
+
+ fixture.querySelector("button")!.click();
+ await nextTick();
+ expect([]).toBeLogged();
+
+ fixture.querySelector("button")!.click();
+ await nextTick();
+ expect([]).toBeLogged();
+ expect(fixture.innerHTML).toBe("1 ");
+
+ def.resolve();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("3 ");
+ expect([
"Child:willRender",
"Child:rendered",
"Parent:willPatch",
"Child:willPatch",
"Child:patched",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
// TODO: rename (remove? seems covered by lifecycle semantics)
@@ -982,12 +925,10 @@ describe("lifecycle hooks", () => {
// TODO: rename (corresponds to https://github.com/odoo/owl/blob/master/doc/reference/concurrency_model.md#semantics)
test("component semantics", async () => {
- let steps: string[] = [];
-
class TestWidget extends Component {
name: string = "test";
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
class B extends TestWidget {
@@ -1031,7 +972,7 @@ describe("lifecycle hooks", () => {
await mount(A, fixture);
expect(fixture.innerHTML).toBe(``);
- expect(steps).toEqual([
+ expect([
"A:setup",
"A:willStart",
"A:willRender",
@@ -1057,13 +998,12 @@ describe("lifecycle hooks", () => {
"C:mounted",
"B:mounted",
"A:mounted",
- ]);
+ ]).toBeLogged();
// update
- steps.splice(0);
c!.state.flag = false;
await nextTick();
- expect(steps).toEqual([
+ expect([
"C:willRender",
"D:willUpdateProps",
"F:setup",
@@ -1080,15 +1020,14 @@ describe("lifecycle hooks", () => {
"F:mounted",
"D:patched",
"C:patched",
- ]);
+ ]).toBeLogged();
});
test("mounted hook is called on every mount, not just the first one", async () => {
- const steps: string[] = [];
class Child extends Component {
static template = xml`child
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -1097,18 +1036,12 @@ describe("lifecycle hooks", () => {
static components = { Child };
state = useState({ hasChild: true });
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
-
- parent.state.hasChild = false;
- await nextTick();
-
- parent.state.hasChild = true;
- await nextTick();
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -1119,12 +1052,22 @@ describe("lifecycle hooks", () => {
"Child:rendered",
"Child:mounted",
"Parent:mounted",
+ ]).toBeLogged();
+
+ parent.state.hasChild = false;
+ await nextTick();
+ expect([
"Parent:willRender",
"Parent:rendered",
"Parent:willPatch",
"Child:willUnmount",
"Child:destroyed",
"Parent:patched",
+ ]).toBeLogged();
+
+ parent.state.hasChild = true;
+ await nextTick();
+ expect([
"Parent:willRender",
"Child:setup",
"Child:willStart",
@@ -1134,7 +1077,6 @@ describe("lifecycle hooks", () => {
"Parent:willPatch",
"Child:mounted",
"Parent:patched",
- ]);
- Object.freeze(steps);
+ ]).toBeLogged();
});
});
diff --git a/tests/components/t_component.test.ts b/tests/components/t_component.test.ts
index 7ae59a2f..126af0cf 100644
--- a/tests/components/t_component.test.ts
+++ b/tests/components/t_component.test.ts
@@ -12,12 +12,10 @@ beforeEach(() => {
describe("t-component", () => {
test("t-component works in simple case", async () => {
- let steps: string[] = [];
-
class Child extends Component {
static template = xml`child
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -25,14 +23,14 @@ describe("t-component", () => {
static template = xml` `;
Child = Child;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
await mount(Parent, fixture);
expect(fixture.innerHTML).toBe("child
");
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -43,23 +41,21 @@ describe("t-component", () => {
"Child:rendered",
"Child:mounted",
"Parent:mounted",
- ]);
+ ]).toBeLogged();
});
test("switching dynamic component", async () => {
- let steps: string[] = [];
-
class ChildA extends Component {
static template = xml`child a
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
class ChildB extends Component {
static template = xml`child b`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -67,19 +63,13 @@ describe("t-component", () => {
static template = xml` `;
Child = ChildA;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
const parent = await mount(Parent, fixture);
-
expect(fixture.innerHTML).toBe("child a
");
-
- parent.Child = ChildB;
- parent.render();
- await nextTick();
- expect(fixture.innerHTML).toBe("child b");
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -90,6 +80,13 @@ describe("t-component", () => {
"ChildA:rendered",
"ChildA:mounted",
"Parent:mounted",
+ ]).toBeLogged();
+
+ parent.Child = ChildB;
+ parent.render();
+ await nextTick();
+ expect(fixture.innerHTML).toBe("child b");
+ expect([
"Parent:willRender",
"ChildB:setup",
"ChildB:willStart",
@@ -101,7 +98,7 @@ describe("t-component", () => {
"ChildA:destroyed",
"ChildB:mounted",
"Parent:patched",
- ]);
+ ]).toBeLogged();
});
test("can switch between dynamic components without the need for a t-key", async () => {
diff --git a/tests/components/t_foreach.test.ts b/tests/components/t_foreach.test.ts
index 7f3d010e..8ace03d0 100644
--- a/tests/components/t_foreach.test.ts
+++ b/tests/components/t_foreach.test.ts
@@ -49,12 +49,10 @@ describe("list of components", () => {
});
test("components in a node in a t-foreach ", async () => {
- const steps: string[] = [];
-
class Child extends Component {
static template = xml`
`;
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
}
@@ -72,7 +70,7 @@ describe("list of components", () => {
static components = { Child };
setup() {
- useLogLifecycle(steps);
+ useLogLifecycle();
}
get items() {
@@ -84,7 +82,7 @@ describe("list of components", () => {
expect(fixture.innerHTML).toBe(
""
);
- expect(steps).toEqual([
+ expect([
"Parent:setup",
"Parent:willStart",
"Parent:willRender",
@@ -100,7 +98,7 @@ describe("list of components", () => {
"Child:mounted",
"Child:mounted",
"Parent:mounted",
- ]);
+ ]).toBeLogged();
});
test("reconciliation alg works for t-foreach in t-foreach", async () => {
diff --git a/tests/helpers.ts b/tests/helpers.ts
index 70d811f5..29555c08 100644
--- a/tests/helpers.ts
+++ b/tests/helpers.ts
@@ -161,53 +161,58 @@ export function snapshotEverything() {
});
}
-export function useLogLifecycle(steps: string[]) {
+const steps: string[] = [];
+
+export function logStep(step: string) {
+ steps.push(step);
+}
+export function useLogLifecycle() {
const component = useComponent();
const name = component.constructor.name;
- steps.push(`${name}:setup`);
+ logStep(`${name}:setup`);
expect(name + ": " + status(component)).toBe(name + ": " + "new");
onWillStart(() => {
expect(name + ": " + status(component)).toBe(name + ": " + "new");
- steps.push(`${name}:willStart`);
+ logStep(`${name}:willStart`);
});
onMounted(() => {
expect(name + ": " + status(component)).toBe(name + ": " + "mounted");
- steps.push(`${name}:mounted`);
+ logStep(`${name}:mounted`);
});
onWillUpdateProps(() => {
expect(name + ": " + status(component)).toBe(name + ": " + "mounted");
- steps.push(`${name}:willUpdateProps`);
+ logStep(`${name}:willUpdateProps`);
});
onWillRender(() => {
- steps.push(`${name}:willRender`);
+ logStep(`${name}:willRender`);
});
onRendered(() => {
- steps.push(`${name}:rendered`);
+ logStep(`${name}:rendered`);
});
onWillPatch(() => {
expect(name + ": " + status(component)).toBe(name + ": " + "mounted");
- steps.push(`${name}:willPatch`);
+ logStep(`${name}:willPatch`);
});
onPatched(() => {
expect(name + ": " + status(component)).toBe(name + ": " + "mounted");
- steps.push(`${name}:patched`);
+ logStep(`${name}:patched`);
});
onWillUnmount(() => {
expect(name + ": " + status(component)).toBe(name + ": " + "mounted");
- steps.push(`${name}:willUnmount`);
+ logStep(`${name}:willUnmount`);
});
onDestroyed(() => {
expect(name + ": " + status(component)).toBe(name + ": " + "destroyed");
- steps.push(`${name}:destroyed`);
+ logStep(`${name}:destroyed`);
});
}
@@ -226,3 +231,55 @@ export async function editInput(input: HTMLInputElement | HTMLTextAreaElement, v
input.dispatchEvent(new Event("change"));
return nextTick();
}
+
+import { diff } from "jest-diff";
+
+afterEach(() => {
+ if (steps.length) {
+ steps.splice(0);
+ throw new Error("Remaining steps! Should be checked by a .toBeLogged() assertion!");
+ }
+});
+
+expect.extend({
+ toBeLogged(expected) {
+ const options = {
+ comment: "steps equality",
+ isNot: this.isNot,
+ promise: this.promise,
+ };
+
+ const currentSteps = steps.splice(0);
+ const pass = this.equals(currentSteps, expected);
+
+ const message = pass
+ ? () =>
+ this.utils.matcherHint("toEqual", undefined, undefined, options) +
+ "\n\n" +
+ `Expected: not ${this.utils.printExpected(expected)}\n` +
+ `Received: ${this.utils.printReceived(currentSteps)}`
+ : () => {
+ const diffString = diff(expected, currentSteps, {
+ expand: this.expand,
+ });
+ return (
+ this.utils.matcherHint("toBe", undefined, undefined, options) +
+ "\n\n" +
+ (diffString && diffString.includes("- Expect")
+ ? `Difference:\n\n${diffString}`
+ : `Expected: ${this.utils.printExpected(expected)}\n` +
+ `Received: ${this.utils.printReceived(currentSteps)}`)
+ );
+ };
+
+ return { actual: currentSteps, message, pass };
+ },
+});
+
+declare global {
+ namespace jest {
+ interface Matchers {
+ toBeLogged(): R;
+ }
+ }
+}
diff --git a/tests/reactivity.test.ts b/tests/reactivity.test.ts
index 2836fe04..f822762b 100644
--- a/tests/reactivity.test.ts
+++ b/tests/reactivity.test.ts
@@ -6,7 +6,7 @@ import {
makeTestFixture,
nextMicroTick,
nextTick,
- snapshotEverything,
+ snapshotEverything
} from "./helpers";
function createReactive(value: any, observer: any = () => {}) {