test: tests that assumed an idle machine wait for what they assert, or carry a budget for their work

Under a busy machine (the gate's lanes side by side, 12 busy cores) these went red at random: a press landed while the button was still busy, an answer was settled before it was asked for, a page grew past one wait's budget, a lazy chunk was transformed inside a 1 s wait, focus was taken a frame after the press. They now wait for the condition they depend on, preload the chunks they open, and the tests that do heavy work (whole-repo scans, the type-aware linter, module resets) carry an explicit budget naming that work. The whole suite four times under 12 busy-loop processes: red 4/4 before, 0/4 after.
This commit is contained in:
2026-10-17 01:08:15 +02:00
parent 926e5e9899
commit 5ba5679239
18 changed files with 181 additions and 96 deletions
@@ -47,6 +47,15 @@ afterEach(() => {
const bodyRows = () => host!.querySelectorAll("tbody tr").length;
// Waits for the page to grow to `n` rows, one background step at a time: each step must come, but on
// a busy machine all of them together can outlast one wait's budget (seen at 190 of 200).
async function grownTo(n: number) {
for (let had = bodyRows(); had < n; had = bodyRows()) {
await waitFor(() => expect(bodyRows()).toBeGreaterThan(had));
}
expect(bodyRows()).toBe(n);
}
// Mounts synchronously, so what the DOM holds right after is the first render alone.
function mount(rows: Row[], columns: Column<Row>[], narrowedBy = "") {
host = document.createElement("div");
@@ -68,7 +77,7 @@ describe("DataTable — a long page mounts in steps", () => {
it("paints its first rows at once and the rest after", async () => {
mount(many(200), plain);
expect(bodyRows()).toBe(FIRST_ROWS);
await waitFor(() => expect(bodyRows()).toBe(200));
await grownTo(200);
});
it("does not re-render the rows it already holds as it grows", async () => {
@@ -84,21 +93,21 @@ describe("DataTable — a long page mounts in steps", () => {
},
];
mount(many(200), counted);
await waitFor(() => expect(bodyRows()).toBe(200));
await grownTo(200);
// The first row was drawn by the first render, not again by each step after it.
expect(renders.get("r0")).toBe(1);
});
it("starts over from the first rows when the caller narrows them, not on new data", async () => {
const draw = mount(many(200), plain, "");
await waitFor(() => expect(bodyRows()).toBe(200));
await grownTo(200);
// New data for the same view (a refetch): the page keeps what it shows.
draw(many(200), "");
expect(bodyRows()).toBe(200);
// A search the caller applied: the urgent render is small again.
draw(many(150, "s"), "s");
expect(bodyRows()).toBe(FIRST_ROWS);
await waitFor(() => expect(bodyRows()).toBe(150));
await grownTo(150);
});
// Channels turns the page to the row a priority pick moved and focuses it in the commit