[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
178
components/vault/VirtualizedHostCollection.test.tsx
Normal file
178
components/vault/VirtualizedHostCollection.test.tsx
Normal file
@@ -0,0 +1,178 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import React from "react";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
|
||||
import {
|
||||
getNextRaggedRowPosition,
|
||||
getNextVirtualHostIndex,
|
||||
resolveVirtualFocusRequest,
|
||||
shouldApplyVirtualHostDomFocus,
|
||||
VirtualizedGroupedHostCollection,
|
||||
VirtualizedHostCollection,
|
||||
} from "./VirtualizedHostCollection.tsx";
|
||||
import { getVaultHostGridColumnCount } from "./vaultHostGridLayout.ts";
|
||||
|
||||
test("virtual focus retries when an active item enters a collection", () => {
|
||||
assert.deepEqual(resolveVirtualFocusRequest({
|
||||
activeItemKey: "host-1",
|
||||
lastRequestedKey: "host-1",
|
||||
itemIndexByKey: new Map(),
|
||||
}), { status: "missing" });
|
||||
|
||||
assert.deepEqual(resolveVirtualFocusRequest({
|
||||
activeItemKey: "host-1",
|
||||
lastRequestedKey: null,
|
||||
itemIndexByKey: new Map([["host-1", 3]]),
|
||||
}), { status: "request", key: "host-1", index: 3 });
|
||||
});
|
||||
|
||||
test("virtual host DOM focus does not steal from outside controls", () => {
|
||||
const inside = { tagName: "BUTTON" } as unknown as Element;
|
||||
const searchInput = { tagName: "INPUT" } as unknown as Element;
|
||||
const body = { tagName: "BODY" } as unknown as Element;
|
||||
const collectionRoot = {
|
||||
contains(node: Node) {
|
||||
return node === inside;
|
||||
},
|
||||
};
|
||||
|
||||
assert.equal(shouldApplyVirtualHostDomFocus({
|
||||
collectionRoot,
|
||||
activeElement: inside,
|
||||
}), true);
|
||||
assert.equal(shouldApplyVirtualHostDomFocus({
|
||||
collectionRoot,
|
||||
activeElement: searchInput,
|
||||
}), false);
|
||||
assert.equal(shouldApplyVirtualHostDomFocus({
|
||||
collectionRoot,
|
||||
activeElement: body,
|
||||
}), true);
|
||||
assert.equal(shouldApplyVirtualHostDomFocus({
|
||||
collectionRoot: null,
|
||||
activeElement: inside,
|
||||
}), false);
|
||||
});
|
||||
|
||||
test("host grids use the same fixed card-width column calculation", () => {
|
||||
assert.equal(getVaultHostGridColumnCount(219), 1);
|
||||
assert.equal(getVaultHostGridColumnCount(452), 2);
|
||||
assert.equal(getVaultHostGridColumnCount(684), 3);
|
||||
assert.equal(getVaultHostGridColumnCount(916), 4);
|
||||
assert.equal(getVaultHostGridColumnCount(1600), 4);
|
||||
});
|
||||
|
||||
test("virtualized host keyboard navigation crosses rows and collection edges", () => {
|
||||
assert.equal(getNextVirtualHostIndex({
|
||||
currentIndex: 1,
|
||||
itemCount: 20,
|
||||
columns: 4,
|
||||
viewMode: "grid",
|
||||
key: "ArrowDown",
|
||||
}), 5);
|
||||
assert.equal(getNextVirtualHostIndex({
|
||||
currentIndex: 5,
|
||||
itemCount: 20,
|
||||
columns: 4,
|
||||
viewMode: "grid",
|
||||
key: "ArrowLeft",
|
||||
}), 4);
|
||||
assert.equal(getNextVirtualHostIndex({
|
||||
currentIndex: 18,
|
||||
itemCount: 20,
|
||||
columns: 4,
|
||||
viewMode: "list",
|
||||
key: "End",
|
||||
}), 19);
|
||||
assert.equal(getNextVirtualHostIndex({
|
||||
currentIndex: 0,
|
||||
itemCount: 20,
|
||||
columns: 1,
|
||||
viewMode: "list",
|
||||
key: "ArrowUp",
|
||||
}), 0);
|
||||
});
|
||||
|
||||
test("grouped grid navigation preserves the actual column across ragged rows", () => {
|
||||
assert.deepEqual(getNextRaggedRowPosition({
|
||||
rowLengths: [1, 3, 3],
|
||||
currentRow: 0,
|
||||
currentColumn: 0,
|
||||
direction: 1,
|
||||
}), { row: 1, column: 0 });
|
||||
assert.deepEqual(getNextRaggedRowPosition({
|
||||
rowLengths: [3, 1, 3],
|
||||
currentRow: 0,
|
||||
currentColumn: 2,
|
||||
direction: 1,
|
||||
}), { row: 1, column: 0 });
|
||||
});
|
||||
|
||||
test("virtualized host grid renders only the viewport window for a large collection", () => {
|
||||
const items = Array.from({ length: 8000 }, (_, index) => ({ id: `host-${index}` }));
|
||||
const html = renderToStaticMarkup(
|
||||
<VirtualizedHostCollection
|
||||
items={items}
|
||||
itemKey={(item) => item.id}
|
||||
scrollRef={React.createRef<HTMLDivElement>()}
|
||||
viewMode="grid"
|
||||
ariaLabel="Hosts"
|
||||
renderItem={(item) => <div data-host-id={item.id} />}
|
||||
/>,
|
||||
);
|
||||
|
||||
const renderedHosts = (html.match(/data-host-id=/g) ?? []).length;
|
||||
assert.ok(renderedHosts > 0);
|
||||
assert.ok(renderedHosts < 100);
|
||||
assert.match(html, /data-vault-virtual-row=/);
|
||||
assert.match(html, /role="grid"/);
|
||||
assert.match(html, /aria-rowcount="2000"/);
|
||||
assert.match(html, /role="gridcell"/);
|
||||
});
|
||||
|
||||
test("virtualized host list keeps one fixed-height item per row", () => {
|
||||
const items = Array.from({ length: 8000 }, (_, index) => ({ id: `host-${index}` }));
|
||||
const html = renderToStaticMarkup(
|
||||
<VirtualizedHostCollection
|
||||
items={items}
|
||||
itemKey={(item) => item.id}
|
||||
scrollRef={React.createRef<HTMLDivElement>()}
|
||||
viewMode="list"
|
||||
ariaLabel="Hosts"
|
||||
renderItem={(item) => <div data-host-id={item.id} />}
|
||||
/>,
|
||||
);
|
||||
|
||||
const renderedHosts = (html.match(/data-host-id=/g) ?? []).length;
|
||||
assert.ok(renderedHosts > 0);
|
||||
assert.ok(renderedHosts < 40);
|
||||
assert.match(html, /grid-template-columns:repeat\(1, minmax\(0, 1fr\)\)/);
|
||||
assert.match(html, /role="list"/);
|
||||
assert.match(html, /aria-setsize="8000"/);
|
||||
});
|
||||
|
||||
test("grouped host grid uses one virtual window across every group", () => {
|
||||
const groups = Array.from({ length: 80 }, (_, groupIndex) => ({
|
||||
name: `group-${groupIndex}`,
|
||||
hosts: Array.from({ length: 100 }, (_, hostIndex) => ({
|
||||
id: `host-${groupIndex}-${hostIndex}`,
|
||||
})),
|
||||
}));
|
||||
const html = renderToStaticMarkup(
|
||||
<VirtualizedGroupedHostCollection
|
||||
groups={groups}
|
||||
itemKey={(item) => item.id}
|
||||
scrollRef={React.createRef<HTMLDivElement>()}
|
||||
viewMode="grid"
|
||||
ariaLabel="Hosts"
|
||||
renderGroupHeader={(group) => <div data-group-name={group.name} />}
|
||||
renderItem={(item) => <div data-host-id={item.id} />}
|
||||
/>,
|
||||
);
|
||||
|
||||
const renderedHosts = (html.match(/data-host-id=/g) ?? []).length;
|
||||
assert.ok(renderedHosts > 0);
|
||||
assert.ok(renderedHosts < 100);
|
||||
assert.equal((html.match(/data-vault-virtual-grouped-collection=/g) ?? []).length, 1);
|
||||
});
|
||||
Reference in New Issue
Block a user