test(desktop): drive native smoke history loading by earliest loaded turn

Problem: the macOS WKWebView smoke intermittently failed its
loading-targeted-history phase on CI (e.g. a5d1ddf58, 6e0941128) while
passing locally.

Root cause: loadHistoryRows required the mounted row count to grow after
every question-rail click. The transcript keeps a fixed-size history window,
so an earlier page can arrive without growing the count, and a click on an
already-loaded turn changes nothing at all; either case stalled the fixture
for the full 30s wait. Whether the phase passed depended on how much history
happened to be loaded when the click landed.

Fix: after each rail click settles, accept an earlier first loaded turn from
the question navigator (the same signal the browser selection replay uses)
or genuine row growth, and let a no-op click fall through to the next,
earlier ratio instead of stalling. Failure output now includes the row count
and earliest loaded turn.

Verification: headless probe of the fixed loop against the bench page
(bench:tools-38t): two no-op clicks on already-loaded turns fall through in
8s each, the third click loads turns 16 -> 0 and grows 549 -> 936 rows;
fixture budget stays well inside the 90s startup watchdog.
This commit is contained in:
SivanCola committed 2026-08-29 05:20:28 +08:00
1 parent 9e94eefad3
commit 9ff2995ab7
1 file changed
+23 -5
+23 -5
View File
@@ -176,15 +176,23 @@
});
const loadHistoryRows = async (element, minimumRows) => {
if (Number.parseInt(element.dataset.transcriptRowCount ?? "0", 10) >= minimumRows) return;
const rowCount = () => Number.parseInt(element.dataset.transcriptRowCount ?? "0", 10);
if (rowCount() >= minimumRows) return;
const rail = await waitFor(() => document.querySelector(".jump-scroll"), 10000);
const earliestLoadedTurn = () => {
const turns = [...document.querySelectorAll(".jump-item[data-loaded='true']")]
.map((marker) => Number(marker.getAttribute("data-turn")))
.filter(Number.isFinite);
return turns.length > 0 ? Math.min(...turns) : null;
};
// Target progressively earlier questions through the real navigation UI.
// A midpoint request usually supplies the 400+ variable-height rows the
// smoke needs without forcing every archived turn into its first geometry
// pass at once.
for (const ratio of [0.65, 0.5, 0.35, 0.2, 0]) {
const before = Number.parseInt(element.dataset.transcriptRowCount ?? "0", 10);
if (before >= minimumRows) return;
if (rowCount() >= minimumRows) return;
const beforeRows = rowCount();
const beforeTurn = earliestLoadedTurn();
const rect = rail.getBoundingClientRect();
rail.dispatchEvent(new MouseEvent("mousedown", {
button: 0,
@@ -193,10 +201,20 @@
bubbles: true,
cancelable: true,
}));
await waitFor(() => Number.parseInt(element.dataset.transcriptRowCount ?? "0", 10) > before, 30000);
await waitFor(() => !document.querySelector(".transcript-navigation-overlay"), 15000);
// A fixed-size history window keeps the mounted row count constant when
// an earlier page arrives, and clicking an already-loaded turn changes
// nothing. Accept an earlier first loaded turn or genuine row growth; a
// no-op click falls through to the next, earlier ratio instead of
// stalling the fixture on one signal.
await waitFor(() => {
const earliest = earliestLoadedTurn();
return rowCount() > beforeRows
|| (earliest !== null && (beforeTurn === null || earliest < beforeTurn));
}, 8000).catch(() => {});
}
throw new Error("native transcript fixture could not load 400 rows");
if (rowCount() >= minimumRows) return;
throw new Error(`native transcript fixture could not load ${minimumRows} rows (rows=${rowCount()} earliestTurn=${earliestLoadedTurn()})`);
};
const start = async () => {