Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions apps/bench/src/__tests__/bench-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,67 @@ describe("bench runtime", () => {
expect(result.metrics.interaction_latency_ms).toBeGreaterThanOrEqual(0);
});

test("measureBenchKeySequenceRun waits for cells that arrive after the viewport", async () => {
// The viewport element attaches before the row model projects its first
// window. One settle frame is not enough for that, so the run used to fail
// for want of a body cell that was about to exist — which is how all three
// selection scripts aborted the comparative runset at zero rendered rows.
document.body.innerHTML = `
<div data-testid="root">
<div data-pretable-scroll-viewport=""></div>
</div>
`;
const root = document.querySelector<HTMLElement>('[data-testid="root"]')!;
const viewport = root.querySelector<HTMLElement>(
"[data-pretable-scroll-viewport]",
)!;
const dispatched: string[] = [];
const MOUNT_FRAME = 30;
let frame = 0;

const previousRaf = globalThis.requestAnimationFrame;
Object.defineProperty(globalThis, "requestAnimationFrame", {
configurable: true,
value: (callback: FrameRequestCallback) => {
frame += 1;
if (frame === MOUNT_FRAME) {
viewport.innerHTML = `
<div data-pretable-row="" data-row-index="0">
<div data-pretable-cell="" tabindex="0">row 0</div>
</div>
`;
viewport
.querySelector<HTMLElement>("[data-pretable-cell]")!
.addEventListener("keydown", (event) => {
dispatched.push((event as KeyboardEvent).key);
});
}
callback(frame * 16);
return frame;
},
});

try {
const result = await measureBenchKeySequenceRun(
root,
"pretable",
"keyboard-nav-row",
{ key: "ArrowDown", shiftKey: false, count: 3, framesBetween: 1 },
);

expect(result.notes).not.toContain(
"no body cell available for keyboard focus",
);
expect(result.status).toBe("completed");
expect(dispatched).toEqual(["ArrowDown", "ArrowDown", "ArrowDown"]);
} finally {
Object.defineProperty(globalThis, "requestAnimationFrame", {
configurable: true,
value: previousRaf,
});
}
});

test("measureBenchKeySequenceRun returns partial when no viewport is present", async () => {
document.body.innerHTML = `<div data-testid="root"></div>`;

Expand Down
14 changes: 11 additions & 3 deletions apps/bench/src/bench-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1966,8 +1966,13 @@ export async function measureBenchKeySequenceRun(
};
}

// Allow the grid to settle and ensure focus is on a body cell.
await waitForAnimationFrame();
// Allow the grid to settle and ensure focus is on a body cell. One frame is
// not settling: the viewport attaches before the row model projects its first
// window, so a single frame leaves the body empty and the run fails below for
// want of a cell that is about to exist. This is the same wait `scroll` and
// `initial` take (#334); all three selection scripts aborted the comparative
// runset at zero rendered rows without it.
await waitForRenderedRowBaseline(root, profile.rowSelector);
const firstCell =
viewport.querySelector<HTMLElement>(
`${profile.cellSelector}[tabindex="0"]`,
Expand Down Expand Up @@ -2095,7 +2100,10 @@ export async function measureBenchAutosizeRun(
metrics: { dom_nodes_peak: root.querySelectorAll("*").length },
};
}
await waitForAnimationFrame();
// Autosize measures the cost of fitting columns to their content, so the
// content has to be on screen first. One frame after mount it is not, and the
// run would time a fit over an empty body.
await waitForRenderedRowBaseline(root, profile.rowSelector);
const start = performance.now();
await autosize();
await waitForAnimationFrame();
Expand Down