Файли
openidea/e2e/terminal-clipboard.spec.ts
kkmihaiтаkkMihai 7af2b098cc
CI / Rust (ubuntu-22.04) (push) Failing after 1h0m17s
CI / Remote server (linux musl) (push) Failing after 25m8s
CI / Cargo deny (push) Successful in 1m39s
CI / Frontend (push) Successful in 25m40s
CI / E2E (push) Failing after 15m24s
CI / Rust (macos-latest) (push) Canceled after 0s
CI / Rust (windows-latest) (push) Canceled after 0s
feat(terminal): paste clipboard media as file paths (#880)
Pasting an image, audio or video from the clipboard into a local terminal saves the bytes to the per-user terminal cache (20 MB limit, files older than 24 h cleaned up on the next paste, private permissions) and inserts the shell-quoted path without running anything. Copied files paste their original paths, plain text pastes unchanged, and SSH sessions keep the existing remote image-paste route. Paths are quoted per shell (zsh/bash, fish, PowerShell, cmd.exe); fish quoting now escapes backslashes and single quotes.

Fixes #838

Co-authored-by: kkMihai <[email protected]>
2026-11-03 02:09:17 +08:00

48 рядки
2.4 KiB
TypeScript

import { expect, test } from "@playwright/test";
test("local terminal stores clipboard media and pastes one shell-quoted argument", async ({ page }) => {
await page.goto("/");
const observed = await page.evaluate(async () => {
const ipcPath = "/src/platform/ipc.ts";
const sessionPath = "/src/features/terminal/session.ts";
const { commands } = await import(ipcPath);
const { TerminalSession } = await import(sessionPath);
Object.defineProperty(window, "__TAURI_INTERNALS__", {
configurable: true,
value: { transformCallback: () => 1 },
});
const stored: { id: number; data: number[]; mime: string }[] = [];
const writes: string[] = [];
commands.termSpawn = async () => ({ status: "ok", data: { id: 838, title: "pwsh", pid: 1 } });
commands.clipboardFileList = async () => ({ status: "ok", data: [] });
commands.termStoreImage = async (id: number, data: number[], mime: string) => {
stored.push({ id, data, mime });
return { status: "ok", data: "C:\\Temp\\a'b c\\image.png" };
};
commands.termWrite = async (_id: number, data: string) => writes.push(data);
commands.termResize = async () => undefined;
commands.termKill = async () => undefined;
const container = document.createElement("div");
container.style.cssText = "position:fixed;inset:0;z-index:99999";
document.body.append(container);
const session = new TerminalSession({ key: "clipboard", cwd: "C:/Temp", onChange() {}, onFocus() {} });
try {
session.attach(container);
await session.start();
const transfer = new DataTransfer();
transfer.items.add(new File([new Uint8Array([137, 80, 78, 71])], "image.png", { type: "image/png" }));
const event = new ClipboardEvent("paste", { bubbles: true, cancelable: true, clipboardData: transfer });
container.querySelector("textarea")!.dispatchEvent(event);
const deadline = Date.now() + 5000;
while (!writes.length && Date.now() < deadline) await new Promise((resolve) => requestAnimationFrame(resolve));
return { stored, writes, claimed: event.defaultPrevented };
} finally {
session.dispose();
container.remove();
}
});
expect(observed.claimed).toBe(true);
expect(observed.stored).toEqual([{ id: 838, data: [137, 80, 78, 71], mime: "image/png" }]);
expect(observed.writes).toEqual(["'C:\\Temp\\a''b c\\image.png'"]);
});