The code cited its plan instead of saying why: `E28 review J4`, `(R6 S3b)`, `S1 justified…`,
`deep-review round 5`. That plan lives in the private ops repo, so for every reader of this one the
citation was noise, and a sentence whose subject was the plan step ("round 2 showed…") hid its
lesson behind an id. Each one is gone and the "why" kept: a bare citation is deleted, a sentence
carrying a lesson is reworded around it ("a later attempt showed…").
Measured (`scripts/audit_metrics.py`, product code): 652 lines in 270 files carried an epic code by
the definition the next commit holds (507 / 241 by the old, two-digit-only one) -> 0. Across all
code, tests, scripts and configs: ~1100 epic-code lines plus ~320 sprint/round ids.
Behaviour unchanged, checked mechanically: every changed .py file has the same AST once docstrings
are blanked; css/sh/toml/yml/json files are identical once comments are stripped; the only
non-comment changes in TS/JS are test titles, one description string in a contract table, and five
ESLint messages, whose text is all the gates snapshot diff holds. Not touched: Markdown (docs and
the changelog keep their history), Alembic migrations, TV episode notation (`S2 · E5`), DESIGN.md
ids (`C1`, `B4`, `S3h`), tracker ids (`SEC-16`, `DX-17`).
One stale claim fixed on the way: test_hls_reaper's docstring said the player checkpoints "every
10 s whether or not it is playing", which plex/stream.py calls wrong; it now says what the code does.
44 lines
1.9 KiB
TypeScript
44 lines
1.9 KiB
TypeScript
// One definition of a PageUp/PageDown page for the palette and every popover list (owner
|
||
// request). The DOM-bound halves are covered where they are wired (CommandPalette, Combobox,
|
||
// ActionMenu); this pins the arithmetic both lean on.
|
||
import { describe, expect, it } from "vitest";
|
||
import { PAGE_FALLBACK, pageStep, pageTarget } from "./listPaging";
|
||
|
||
const box = (top: number, height: number) =>
|
||
({ getBoundingClientRect: () => ({ top, bottom: top + height, height }) }) as unknown as Element;
|
||
|
||
describe("pageStep", () => {
|
||
it("is the rows wholly in view minus one", () => {
|
||
// A 100px list with 30px rows: 0–30, 30–60, 60–90 fit; 90–120 is cut off.
|
||
const rows = [0, 30, 60, 90, 120].map((top) => box(top, 30));
|
||
expect(pageStep(box(0, 100), rows)).toBe(2);
|
||
});
|
||
|
||
it("never stands still, and falls back without layout", () => {
|
||
expect(pageStep(box(0, 20), [box(0, 30)])).toBe(1);
|
||
expect(pageStep(box(0, 0), [box(0, 30)])).toBe(PAGE_FALLBACK);
|
||
expect(pageStep(null, [])).toBe(PAGE_FALLBACK);
|
||
});
|
||
});
|
||
|
||
describe("pageTarget", () => {
|
||
it("moves a page and stops at both ends instead of wrapping", () => {
|
||
expect(pageTarget(0, 10, 25)).toBe(10);
|
||
expect(pageTarget(20, 10, 25)).toBe(24);
|
||
expect(pageTarget(24, 10, 25)).toBe(24);
|
||
expect(pageTarget(3, -10, 25)).toBe(0);
|
||
expect(pageTarget(0, 10, 0)).toBeNull();
|
||
});
|
||
|
||
it("passes over a row it may not land on, then looks back — never past the start", () => {
|
||
const off = new Set([10, 11]);
|
||
const usable = (i: number) => !off.has(i);
|
||
expect(pageTarget(0, 10, 25, usable)).toBe(12); // on past the landing row first
|
||
off.add(24);
|
||
off.add(23);
|
||
expect(pageTarget(15, 10, 25, usable)).toBe(22); // at the end: back toward the start
|
||
expect(pageTarget(22, 10, 25, usable)).toBeNull(); // nothing between the start and the end
|
||
expect(pageTarget(15, -10, 25, (i) => i > 12)).toBe(13);
|
||
});
|
||
});
|