Skip to content

Commit 49fa0ed

Browse files
committed
test(runtime): harden reconciliation edge cases
1 parent a63a2f6 commit 49fa0ed

7 files changed

Lines changed: 485 additions & 110 deletions

File tree

packages/core/src/runtime/__tests__/reconcile.composite.test.ts

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { assert, describe, test } from "@rezi-ui/testkit";
2-
import { encodeZrevBatchV1, flushMicrotasks, makeBackendBatch } from "../../app/__tests__/helpers.js";
2+
import {
3+
encodeZrevBatchV1,
4+
flushMicrotasks,
5+
makeBackendBatch,
6+
} from "../../app/__tests__/helpers.js";
37
import { StubBackend } from "../../app/__tests__/stubBackend.js";
48
import { createApp } from "../../app/createApp.js";
5-
import { defineWidget, type VNode, ui } from "../../index.js";
9+
import { type VNode, defineWidget, ui } from "../../index.js";
610
import { type RuntimeInstance, commitVNodeTree } from "../commit.js";
711
import { createInstanceIdAllocator } from "../instance.js";
812
import { createCompositeInstanceRegistry, runPendingEffects } from "../instances.js";
@@ -47,7 +51,11 @@ function createHarness(start = 1) {
4751
};
4852
}
4953

50-
function commitComposite(prevRoot: RuntimeInstance | null, vnode: VNode, harness: ReturnType<typeof createHarness>) {
54+
function commitComposite(
55+
prevRoot: RuntimeInstance | null,
56+
vnode: VNode,
57+
harness: ReturnType<typeof createHarness>,
58+
) {
5159
const res = commitVNodeTree(prevRoot, vnode, {
5260
allocator: harness.allocator,
5361
collectLifecycleInstanceIds: true,
@@ -60,7 +68,6 @@ function commitComposite(prevRoot: RuntimeInstance | null, vnode: VNode, harness
6068
},
6169
});
6270

63-
assert.equal(res.ok, true);
6471
if (!res.ok) {
6572
assert.fail(`commit failed: ${res.fatal.code}: ${res.fatal.detail}`);
6673
throw new Error("unreachable");
@@ -84,10 +91,16 @@ function deleteUnmounted(
8491
describe("reconciliation - defineWidget/composite", () => {
8592
test("same widget + different props reuses widget instance", () => {
8693
const harness = createHarness();
87-
const Banner = defineWidget<{ label: string; key?: string }>((props) => ui.text(`label:${props.label}`));
94+
const Banner = defineWidget<{ label: string; key?: string }>((props) =>
95+
ui.text(`label:${props.label}`),
96+
);
8897

8998
const c0 = commitComposite(null, widgetHost([Banner({ label: "a", key: "banner" })]), harness);
90-
const c1 = commitComposite(c0.root, widgetHost([Banner({ label: "b", key: "banner" })]), harness);
99+
const c1 = commitComposite(
100+
c0.root,
101+
widgetHost([Banner({ label: "b", key: "banner" })]),
102+
harness,
103+
);
91104

92105
const id0 = firstChildId(c0.root);
93106
const id1 = firstChildId(c1.root);
@@ -109,18 +122,26 @@ describe("reconciliation - defineWidget/composite", () => {
109122
return ui.text(`count:${String(count)}`);
110123
});
111124

112-
const c0 = commitComposite(null, widgetHost([Counter({ initial: 1, key: "counter" })]), harness);
125+
const c0 = commitComposite(
126+
null,
127+
widgetHost([Counter({ initial: 1, key: "counter" })]),
128+
harness,
129+
);
113130
const widgetId = firstChildId(c0.root);
114131

115-
assert.ok(setCount !== null, "expected setCount to be captured");
116-
if (!setCount) {
132+
const applySetCount = setCount as ((v: number) => void) | null;
133+
if (applySetCount === null) {
117134
assert.fail("expected setCount to be captured");
118135
return;
119136
}
120-
setCount(7);
137+
applySetCount(7);
121138
assert.equal(harness.invalidated.includes(widgetId), true);
122139

123-
const c1 = commitComposite(c0.root, widgetHost([Counter({ initial: 999, key: "counter" })]), harness);
140+
const c1 = commitComposite(
141+
c0.root,
142+
widgetHost([Counter({ initial: 999, key: "counter" })]),
143+
harness,
144+
);
124145

125146
assert.equal(firstChildId(c1.root), widgetId);
126147
assert.deepEqual(seen, [1, 7]);
@@ -288,12 +309,12 @@ describe("reconciliation - defineWidget/composite", () => {
288309
const c1 = commitComposite(c0.root, widgetHost([New({ key: "slot" })]), harness);
289310

290311
const invalidatedBefore = harness.invalidated.length;
291-
assert.ok(staleSetter !== null, "expected stale setter from old widget");
292-
if (!staleSetter) {
312+
const invokeStaleSetter = staleSetter as ((v: number) => void) | null;
313+
if (invokeStaleSetter === null) {
293314
assert.fail("expected stale setter from old widget");
294315
return;
295316
}
296-
staleSetter(123);
317+
invokeStaleSetter(123);
297318

298319
commitComposite(c1.root, widgetHost([New({ key: "slot" })]), harness);
299320
assert.equal(harness.invalidated.length, invalidatedBefore);
@@ -325,7 +346,9 @@ describe("reconciliation - defineWidget/composite", () => {
325346

326347
test("same unkeyed widget at same position is reused", () => {
327348
const harness = createHarness();
328-
const Plain = defineWidget<{ value: number }>((props) => ui.text(`v:${String(props.value)}`));
349+
const Plain = defineWidget<{ value: number; key?: string }>((props) =>
350+
ui.text(`v:${String(props.value)}`),
351+
);
329352

330353
const c0 = commitComposite(null, widgetHost([Plain({ value: 1 })]), harness);
331354
const c1 = commitComposite(c0.root, widgetHost([Plain({ value: 2 })]), harness);
@@ -367,8 +390,16 @@ describe("reconciliation - defineWidget/composite", () => {
367390
const harness = createHarness();
368391
const Widget = defineWidget<{ key?: string }>(() => ui.text("stable"));
369392

370-
const c0 = commitComposite(null, widgetHost([Widget({ key: "w" }), ui.text("tick:0")]), harness);
371-
const c1 = commitComposite(c0.root, widgetHost([Widget({ key: "w" }), ui.text("tick:1")]), harness);
393+
const c0 = commitComposite(
394+
null,
395+
widgetHost([Widget({ key: "w" }), ui.text("tick:0")]),
396+
harness,
397+
);
398+
const c1 = commitComposite(
399+
c0.root,
400+
widgetHost([Widget({ key: "w" }), ui.text("tick:1")]),
401+
harness,
402+
);
372403

373404
assert.equal(childIdByKey(c1.root, "w"), childIdByKey(c0.root, "w"));
374405
});

0 commit comments

Comments
 (0)