129 lines
3.7 KiB
TypeScript
129 lines
3.7 KiB
TypeScript
import { expect, test } from "bun:test"
|
|
import {
|
|
createClipboard,
|
|
type ClipboardReadOptions,
|
|
type ClipboardReadResult,
|
|
type ClipboardService as CoreClipboardService,
|
|
type ClipboardWriteOptions,
|
|
type ClipboardWriteResult,
|
|
type HostClipboardService,
|
|
} from "@opentui/core"
|
|
import { createClipboardAdapter } from "../src/clipboard"
|
|
|
|
function coreClipboard(options: {
|
|
read?: ClipboardReadResult
|
|
write?: ClipboardWriteResult
|
|
onRead?: (input: ClipboardReadOptions) => void
|
|
onWrite?: (text: string, input: ClipboardWriteOptions) => void
|
|
}): CoreClipboardService {
|
|
return {
|
|
async read(input) {
|
|
options.onRead?.(input)
|
|
return options.read ?? { status: "empty" }
|
|
},
|
|
async writeText(text, input) {
|
|
options.onWrite?.(text, input)
|
|
return (
|
|
options.write ?? {
|
|
host: { status: "written" },
|
|
terminal: { status: "not-attempted", capability: "unknown" },
|
|
}
|
|
)
|
|
},
|
|
async clear() {
|
|
return {
|
|
host: { status: "cleared" },
|
|
terminal: { status: "not-attempted", capability: "unknown" },
|
|
}
|
|
},
|
|
async dispose() {},
|
|
}
|
|
}
|
|
|
|
test("adapts OpenTUI image and text reads", async () => {
|
|
const requests: ClipboardReadOptions[] = []
|
|
const image = createClipboardAdapter(
|
|
coreClipboard({
|
|
read: {
|
|
status: "read",
|
|
representation: { mimeType: "image/png", bytes: new Uint8Array([0, 1, 2, 255]) },
|
|
},
|
|
onRead: (input) => requests.push(input),
|
|
}),
|
|
)
|
|
const text = "line 1\r\n\t世界"
|
|
const plain = createClipboardAdapter(
|
|
coreClipboard({
|
|
read: {
|
|
status: "read",
|
|
representation: { mimeType: "text/plain", bytes: new TextEncoder().encode(text) },
|
|
},
|
|
}),
|
|
)
|
|
|
|
expect(await image.read()).toEqual({ data: "AAEC/w==", mime: "image/png" })
|
|
expect(await plain.read()).toEqual({ data: text, mime: "text/plain" })
|
|
expect(requests).toEqual([{ preferredTypes: ["image/png", "text/plain"], selection: "clipboard" }])
|
|
})
|
|
|
|
test("uses all available routes but skips the process host remotely", async () => {
|
|
const writes = { host: 0, terminal: 0 }
|
|
const host: HostClipboardService = {
|
|
maxWriteBytes: 8 * 1024 * 1024,
|
|
async read() {
|
|
return { status: "empty" }
|
|
},
|
|
async writeText() {
|
|
writes.host++
|
|
return { status: "written" }
|
|
},
|
|
async clear() {
|
|
return { status: "cleared" }
|
|
},
|
|
async dispose() {},
|
|
}
|
|
const clipboard = createClipboardAdapter(
|
|
createClipboard({
|
|
host,
|
|
terminal: {
|
|
remote: true,
|
|
writeText() {
|
|
writes.terminal++
|
|
return { status: "attempted", capability: "supported" }
|
|
},
|
|
clear() {
|
|
return { status: "attempted", capability: "supported" }
|
|
},
|
|
},
|
|
}),
|
|
)
|
|
|
|
expect(await clipboard.write("hello")).toBeUndefined()
|
|
expect(writes).toEqual({ host: 0, terminal: 1 })
|
|
})
|
|
|
|
test("rejects only when no clipboard route accepted the write", async () => {
|
|
const writes: [string, ClipboardWriteOptions][] = []
|
|
const failure = new Error("native clipboard failed")
|
|
const fallback = createClipboardAdapter(
|
|
coreClipboard({
|
|
write: {
|
|
host: { status: "written" },
|
|
terminal: { status: "local-failure", capability: "supported" },
|
|
},
|
|
}),
|
|
)
|
|
const clipboard = createClipboardAdapter(
|
|
coreClipboard({
|
|
write: {
|
|
host: { status: "failed", error: failure },
|
|
terminal: { status: "local-failure", capability: "supported" },
|
|
},
|
|
onWrite: (text, input) => writes.push([text, input]),
|
|
}),
|
|
)
|
|
|
|
expect(await fallback.write("hello")).toBeUndefined()
|
|
expect(await clipboard.write("hello").then(undefined, (error) => error)).toBe(failure)
|
|
expect(writes).toEqual([["hello", { destination: "all-available", selection: "clipboard" }]])
|
|
})
|