mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-10 16:03:57 +00:00
81 lines
4.3 KiB
TypeScript
81 lines
4.3 KiB
TypeScript
import { expect, test } from "bun:test"
|
|
import { rm } from "node:fs/promises"
|
|
import path from "node:path"
|
|
import { Browser } from "@opencode/plugin-browser/rpc"
|
|
import { browserFailure, protocolError } from "../src/main/browser/errors"
|
|
import { createBrowserFiles } from "../src/main/browser/files"
|
|
import { analyzeCpu, analyzeTrace, parseHeap } from "../src/main/browser/analysis"
|
|
|
|
const tabID = Browser.TabID.make(`tab_${crypto.randomUUID()}`)
|
|
|
|
test("navigation failures explain the server network and never recommend disabling TLS", () => {
|
|
const action: Browser.Action = { type: "navigate", tabID, url: "https://example.com" }
|
|
const refused = browserFailure(action, new Error("net::ERR_CONNECTION_REFUSED"))
|
|
expect(refused.code).toBe("navigation_failed")
|
|
expect(refused.message).toContain("localhost means that server")
|
|
expect(refused.message).toContain("hostname/port")
|
|
const tls = browserFailure(action, new Error("net::ERR_CERT_AUTHORITY_INVALID"))
|
|
expect(tls.message).toContain("do not bypass certificate checks")
|
|
const aborted = browserFailure(action, new Error("net::ERR_ABORTED"))
|
|
expect(aborted.message).toContain("browser.files.list({tabID})")
|
|
})
|
|
|
|
test("native protocol errors keep the cause and give a valid recovery operation", () => {
|
|
expect(protocolError("DOM.resolveNode", new Error("Could not find node with given id")).message).toContain(
|
|
"browser.snapshot({tabID})",
|
|
)
|
|
expect(protocolError("Runtime.evaluate", new Error("Cannot find context with specified id")).message).toContain(
|
|
"browser.frames({tabID})",
|
|
)
|
|
const unsupported = protocolError("Target.getBrowserContexts", new Error("Not allowed"))
|
|
expect(unsupported.message).toContain("does not support or allow")
|
|
expect(unsupported.message).toContain("do not retry unchanged")
|
|
expect(unsupported.cause).toBeInstanceOf(Error)
|
|
const failure = browserFailure({ type: "screenshot", tabID }, new Error("UnknownVizError"))
|
|
expect(failure.message).toContain("browser.tabs.focus({tabID})")
|
|
expect(failure.message).toContain("report the capture failure")
|
|
})
|
|
|
|
test("long page errors retain the operation context without classifying script text as a navigation error", () => {
|
|
const failure = browserFailure(
|
|
{ type: "evaluate", tabID, script: "throw Error()" },
|
|
new Error("ERR_CONNECTION_REFUSED " + "x".repeat(10_000)),
|
|
)
|
|
expect(failure.code).toBe("operation_failed")
|
|
expect(failure.message.startsWith("browser.evaluate failed.")).toBe(true)
|
|
expect(failure.message.length).toBeLessThanOrEqual(2_048)
|
|
})
|
|
|
|
test("files distinguish pending, failed, unknown and missing desktop copies", async () => {
|
|
const files = createBrowserFiles(() => ["https://example.com"])
|
|
await files.ready
|
|
try {
|
|
const pending = files.add("download.txt", "text/plain")
|
|
expect(() => files.get(pending.id)).toThrow("do not start a duplicate download")
|
|
pending.state = "failed"
|
|
expect(() => files.get(pending.id)).toThrow("Inspect browser.console")
|
|
expect(() => files.get(Browser.FileID.make(`file_${crypto.randomUUID()}`))).toThrow(
|
|
"not a server path or request ID",
|
|
)
|
|
const id = await files.save("capture.json", "application/json", new TextEncoder().encode("{}"))
|
|
await rm(files.get(id).path)
|
|
await expect(files.transfer(id)).rejects.toThrow("on the desktop")
|
|
await expect(
|
|
files.save("large.bin", "application/octet-stream", new Uint8Array(Browser.MAX_FILE_BYTES + 1)),
|
|
).rejects.toThrow("Do not retry an identical capture")
|
|
// The page sees the temp basename as File.name: spaces and non-ASCII stay, only characters no
|
|
// filesystem accepts change, and a bare dot or separator cannot leave the per-file directory.
|
|
expect(path.basename(files.add("Quarter 1 日本語.csv", "text/csv").path)).toBe("Quarter 1 日本語.csv")
|
|
expect(path.basename(files.add("a/b:c?.txt", "text/plain").path)).toBe("a_b_c_.txt")
|
|
expect(path.basename(files.add("..", "text/plain").path)).toBe("file")
|
|
expect(path.dirname(files.add(".", "text/plain").path).startsWith(files.directory)).toBe(true)
|
|
} finally {
|
|
await files.dispose()
|
|
}
|
|
})
|
|
|
|
test("wrong capture formats identify the matching capture tool", () => {
|
|
expect(() => analyzeTrace({ nodes: [] })).toThrow("browser.trace.stop")
|
|
expect(() => analyzeCpu({ traceEvents: [] })).toThrow("browser.cpu.stop")
|
|
expect(() => parseHeap({ traceEvents: [] })).toThrow("browser.heap.snapshot")
|
|
})
|