Files
NetMesh/components/GlobalSftpTransferCenter.accessibility.test.ts
zhaolei 3c72efcb7f
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
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

119 lines
4.7 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import React from "react";
import {
createDomRenderer,
dispatchDomEvent,
flushEffects,
installDomEnvironment,
} from "./test-support/renderReactDom.tsx";
test("associates the transfer-center folder warning with every destructive Replace action", async (t) => {
const env = installDomEnvironment();
const previousMutationObserver = globalThis.MutationObserver;
const previousNodeFilter = globalThis.NodeFilter;
const previousHTMLInputElement = globalThis.HTMLInputElement;
const previousElement = globalThis.Element;
const previousRequestAnimationFrame = globalThis.requestAnimationFrame;
const previousCancelAnimationFrame = globalThis.cancelAnimationFrame;
Object.defineProperties(globalThis, {
MutationObserver: { configurable: true, writable: true, value: env.window.MutationObserver },
NodeFilter: { configurable: true, writable: true, value: env.window.NodeFilter },
HTMLInputElement: { configurable: true, writable: true, value: env.window.HTMLInputElement },
Element: { configurable: true, writable: true, value: env.window.Element },
requestAnimationFrame: {
configurable: true,
writable: true,
value: (callback: FrameRequestCallback) => setTimeout(() => callback(Date.now()), 0),
},
cancelAnimationFrame: { configurable: true, writable: true, value: clearTimeout },
});
const { I18nProvider } = await import("../application/i18n/I18nProvider.tsx");
const { sftpTransferCenterStore } = await import("../application/state/sftpTransferCenterStore.ts");
const { GlobalSftpTransferCenter } = await import("./GlobalSftpTransferCenter.tsx");
const { TooltipProvider } = await import("./ui/tooltip.tsx");
const renderer = await createDomRenderer(env.document);
const taskId = "accessible-folder-conflict";
const unregisterOwner = sftpTransferCenterStore.registerOwner("accessibility-test-owner", {
pause: () => {},
resume: () => {},
cancel: () => {},
retry: () => {},
prioritize: () => {},
dismiss: () => {},
ownsTask: () => true,
resolveConflict: () => {},
});
sftpTransferCenterStore.publishOwner("accessibility-test-owner", [{
id: taskId,
fileName: "docs",
sourcePath: "/source/docs",
targetPath: "/destination/docs",
sourceConnectionId: "source",
targetConnectionId: "target",
direction: "remote-to-remote",
status: "attention",
totalBytes: 0,
transferredBytes: 0,
speed: 0,
startTime: 1,
isDirectory: true,
conflict: {
transferId: taskId,
fileName: "docs",
sourcePath: "/source/docs",
targetPath: "/destination/docs",
isDirectory: true,
existingType: "directory",
applyToAllCount: 2,
existingSize: 0,
newSize: 0,
existingModified: 1,
newModified: 2,
},
}]);
t.after(async () => {
await renderer.unmount();
sftpTransferCenterStore.dismiss(taskId);
unregisterOwner();
await new Promise((resolve) => setTimeout(resolve, 50));
Object.defineProperties(globalThis, {
MutationObserver: { configurable: true, writable: true, value: previousMutationObserver },
NodeFilter: { configurable: true, writable: true, value: previousNodeFilter },
HTMLInputElement: { configurable: true, writable: true, value: previousHTMLInputElement },
Element: { configurable: true, writable: true, value: previousElement },
requestAnimationFrame: { configurable: true, writable: true, value: previousRequestAnimationFrame },
cancelAnimationFrame: { configurable: true, writable: true, value: previousCancelAnimationFrame },
});
env.cleanup();
});
await renderer.render(React.createElement(
I18nProvider,
{ locale: "en" },
React.createElement(TooltipProvider, null, React.createElement(GlobalSftpTransferCenter)),
));
await flushEffects();
const toggle = env.document.querySelector<HTMLButtonElement>("[data-section=global-sftp-transfer-toggle]");
assert.ok(toggle, "transfer-center toggle should render");
await dispatchDomEvent(toggle, new env.window.MouseEvent("click", { bubbles: true }));
await flushEffects();
const replaceButtons = Array.from(env.document.querySelectorAll<HTMLButtonElement>("button"))
.filter((button) => button.textContent?.startsWith("Replace"));
assert.equal(replaceButtons.length, 2);
for (const button of replaceButtons) {
const describedBy = button.getAttribute("aria-describedby");
assert.ok(describedBy, `${button.textContent} should expose the folder deletion warning`);
const warning = env.document.getElementById(describedBy);
assert.match(
warning?.textContent ?? "",
/Replace: deletes destination-only content and cannot be undone/,
);
}
});