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
51 changes: 51 additions & 0 deletions e2e/specs/grid-selection.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,33 @@ async function cellAt(toplevel, position, cell) {
}

/** The number cell selects and never opens an editor, which is why every gesture below lands on it. */
/** How many times the grid has been committed, which `CueList.tsx` counts on the element. */
function gridRenders() {
return browser.execute(() => Number(document.documentElement.dataset.gridRenders) || 0);
}

/** Wait until the grid stops drawing on its own, so the count below is about the click. */
async function settle() {
let last = -1;
for (;;) {
const now = await gridRenders();
if (now === last) {
return;
}
last = now;
await browser.pause(400);
}
}

/** The list position the cursor is on, or null. */
function cursorRow() {
return browser.execute(() => {
const row = document.querySelector(".cuelist__row--active");
const text = row?.querySelector(".cuelist__pos")?.textContent;
return text === undefined || text === null ? null : Number(text);
});
}

async function clickRow(toplevel, position, modifier = null) {
const at = await cellAt(toplevel, position, ".cuelist__pos");
if (modifier === null) {
Expand Down Expand Up @@ -226,4 +253,28 @@ describe("the grid's selection gestures", () => {
});
expect(await present(".statusbar__dirty")).toBe(false);
});

it("draws nothing again when the click lands on the row the cursor already holds", async () => {
// Interface-spec 7.2: announcing the same selection again is a redundant render in React, and a
// render that happened for nothing cannot be seen from outside. The grid counts its own commits
// on the element, which is what makes this readable at all. See BACKLOG.md N159.
await clickRow(toplevel, 2);
await waitFor(async () => ((await cursorRow()) === 2 ? 1 : null), {
timeout: 15000,
message: "the cursor to land on the second row",
});
await settle();

const before = await gridRenders();
await clickRow(toplevel, 2);
await browser.pause(1000);
expect(await gridRenders()).toBe(before);

// The control, and it is what says the counter counts: another row does draw again.
await clickRow(toplevel, 3);
await waitFor(async () => ((await gridRenders()) > before ? 1 : null), {
timeout: 15000,
message: "a click on another row to draw the grid again",
});
});
});
2 changes: 1 addition & 1 deletion e2e/wdio.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
* Every spec that exists must run. WebdriverIO does not reliably fail a run that executed nothing,
* so the count is asserted here. Bump it when you add a test; see e2e/README.md.
*/
const EXPECTED_TESTS = 515;
const EXPECTED_TESTS = 516;

/** How long mocha lets one test live. Every wait inside a test must be shorter. See N165. */
const TEST_LIMIT_MS = 60000;
Expand Down
7 changes: 7 additions & 0 deletions src/components/CueList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ export default function CueList({
}: CueListProps) {
const listRef = useRef<HTMLDivElement>(null);
const editorRef = useRef<HTMLTextAreaElement>(null);
// Every commit of the grid, counted where a check can read it. Interface-spec 7.2 asks a click on
// the row the cursor already holds to change nothing, and a render that happened for nothing is
// invisible from outside by definition. No dependency list: this counts commits, not values.
useEffect(() => {
const root = document.documentElement;
root.dataset.gridRenders = String((Number(root.dataset.gridRenders) || 0) + 1);
});
const [scrollTop, setScrollTop] = useState(0);
const [viewport, setViewport] = useState(0);
const [editing, setEditing] = useState<number | null>(null);
Expand Down
6 changes: 5 additions & 1 deletion src/hooks/useCueSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ export function useCueSelection(count: number, openId: number): CueSelection {
setSelected(runBetween(anchor.current, index));
} else if (how === "plain") {
anchor.current = index;
setSelected(new Set([index]));
// The same one row again is the same selection. A new Set with the same member is a new
// object, and React renders everything that reads it for nothing (interface-spec 7.2).
setSelected((current) =>
current.size === 1 && current.has(index) ? current : new Set([index]),
);
}
}, []);

Expand Down
Loading