Files
buzz/desktop/src/features/messages/lib/virtualizedTimelineItems.test.mjs
Tyler b0ad2b66dd Retain a stable message window in channel timelines (#1698)
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-12 21:41:38 -04:00

207 lines
6.2 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import {
buildVirtualizedItems,
didPrependVirtualizedTimeline,
virtualizedItemKey,
} from "./virtualizedTimelineItems.ts";
// Timestamps: day A = 2026-06-01, day B = 2026-06-02, day Z = 2026-05-31.
function dayAt(year, month, day, hour = 12, minute = 0) {
return Math.floor(
new Date(year, month - 1, day, hour, minute, 0).getTime() / 1_000,
);
}
function messageItem(key) {
return {
kind: "message",
key,
entry: { message: { id: key } },
isContinuation: false,
isFollowedByContinuation: false,
};
}
function group(dayKey, headingTimestamp, itemKeys) {
return {
key: dayKey,
headingTimestamp,
items: itemKeys.map(messageItem),
};
}
const DAY_A = dayAt(2026, 6, 1);
const DAY_B = dayAt(2026, 6, 2);
const DAY_Z = dayAt(2026, 5, 31);
function keysOf(dayGroups, { leading = undefined, exhausted = false } = {}) {
return buildVirtualizedItems(dayGroups, leading, exhausted).map(
virtualizedItemKey,
);
}
/**
* Virtua's `shift` moves every cached slot uniformly by the length delta.
* With shift admitted, assert every previous key's cached height lands on the
* SAME logical item in the new list.
*/
function assertShiftAdmittedAndCacheClean(previousKeys, keys) {
assert.equal(didPrependVirtualizedTimeline(previousKeys, keys), true);
const delta = keys.length - previousKeys.length;
previousKeys.forEach((key, index) => {
assert.equal(
keys[index + delta],
key,
`cached height at slot ${index} (${key}) would land on ${keys[index + delta]}`,
);
});
}
test("oldest day carries no divider while more history exists", () => {
const keys = keysOf([group("day-A", DAY_A, ["a3", "a4", "a5"])]);
assert.deepEqual(keys, ["a3", "a4", "a5", "bottom-spacer"]);
});
test("oldest day divider renders once history is exhausted", () => {
const keys = keysOf([group("day-A", DAY_A, ["a3", "a4", "a5"])], {
exhausted: true,
});
assert.deepEqual(keys, [
"day-divider:day-A",
"a3",
"a4",
"a5",
"bottom-spacer",
]);
});
test("interior day boundaries always render dividers", () => {
const keys = keysOf([
group("day-A", DAY_A, ["a3"]),
group("day-B", DAY_B, ["b1", "b2"]),
]);
assert.deepEqual(keys, [
"a3",
"day-divider:day-B",
"b1",
"b2",
"bottom-spacer",
]);
});
test("undated group never renders a divider even when proven", () => {
const keys = keysOf(
[group("day-undated", null, ["u1"]), group("day-B", DAY_B, ["b1"])],
{ exhausted: true },
);
assert.deepEqual(keys, ["u1", "day-divider:day-B", "b1", "bottom-spacer"]);
});
test("same-day prepend into the oldest day admits shift with a clean cache", () => {
const previous = keysOf([
group("day-A", DAY_A, ["a3", "a4", "a5"]),
group("day-B", DAY_B, ["b1"]),
]);
const next = keysOf([
group("day-A", DAY_A, ["a1", "a2", "a3", "a4", "a5"]),
group("day-B", DAY_B, ["b1"]),
]);
assertShiftAdmittedAndCacheClean(previous, next);
});
test("cross-day prepend materializes the newly proven divider as pure prefix", () => {
const previous = keysOf([
group("day-A", DAY_A, ["a3", "a4", "a5"]),
group("day-B", DAY_B, ["b1"]),
]);
// Day Z loads before day A: day A's boundary is now proven, so BOTH the
// z-rows and day A's divider enter as prefix.
const next = keysOf([
group("day-Z", DAY_Z, ["z1", "z2"]),
group("day-A", DAY_A, ["a3", "a4", "a5"]),
group("day-B", DAY_B, ["b1"]),
]);
assert.deepEqual(
next.slice(0, 4),
["z1", "z2", "day-divider:day-A", "a3"],
"day-Z itself is now the unproven oldest day and carries no divider",
);
assertShiftAdmittedAndCacheClean(previous, next);
});
test("mixed page (older day rows + same-day completion) admits shift", () => {
const previous = keysOf([
group("day-A", DAY_A, ["a3", "a4", "a5"]),
group("day-B", DAY_B, ["b1"]),
]);
const next = keysOf([
group("day-Z", DAY_Z, ["z9"]),
group("day-A", DAY_A, ["a1", "a2", "a3", "a4", "a5"]),
group("day-B", DAY_B, ["b1"]),
]);
assertShiftAdmittedAndCacheClean(previous, next);
});
test("history exhaustion with an empty page inserts the divider as pure prefix", () => {
const groups = [
group("day-A", DAY_A, ["a3", "a4", "a5"]),
group("day-B", DAY_B, ["b1"]),
];
const previous = keysOf(groups);
const next = keysOf(groups, { exhausted: true });
assert.deepEqual(next.slice(0, 2), ["day-divider:day-A", "a3"]);
assertShiftAdmittedAndCacheClean(previous, next);
});
test("leading content arriving with the final page stays a pure prefix", () => {
const previous = keysOf([
group("day-A", DAY_A, ["a3", "a4", "a5"]),
group("day-B", DAY_B, ["b1"]),
]);
const next = keysOf(
[
group("day-A", DAY_A, ["a1", "a2", "a3", "a4", "a5"]),
group("day-B", DAY_B, ["b1"]),
],
{ leading: "intro", exhausted: true },
);
assert.deepEqual(next.slice(0, 2), ["leading-content", "day-divider:day-A"]);
assertShiftAdmittedAndCacheClean(previous, next);
});
test("divider count and order match proven boundaries exactly", () => {
const items = buildVirtualizedItems(
[
group("day-Z", DAY_Z, ["z1"]),
group("day-A", DAY_A, ["a1"]),
group("day-B", DAY_B, ["b1"]),
],
undefined,
true,
);
const dividers = items.filter((item) => item.kind === "day-divider");
assert.deepEqual(
dividers.map((item) => item.key),
["day-divider:day-Z", "day-divider:day-A", "day-divider:day-B"],
);
// Each divider precedes its day's first message.
const keys = items.map(virtualizedItemKey);
assert.equal(keys.indexOf("day-divider:day-Z"), keys.indexOf("z1") - 1);
assert.equal(keys.indexOf("day-divider:day-A"), keys.indexOf("a1") - 1);
assert.equal(keys.indexOf("day-divider:day-B"), keys.indexOf("b1") - 1);
});
test("naive counterexample stays rejected: same key cannot precede prepended rows", () => {
// Regression pin for the shape Wren vetoed: if the oldest day's divider
// existed BEFORE the same-day prepend, admission must fail.
assert.equal(
didPrependVirtualizedTimeline(
["day-divider:day-A", "a3", "a4", "a5", "bottom-spacer"],
["day-divider:day-A", "a1", "a2", "a3", "a4", "a5", "bottom-spacer"],
),
false,
);
});