mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-08 17:34:51 +00:00
The browser pane measures the selected element in page CSS pixels, but capturePage takes device-independent pixels. Paseo's zoom carries into the in-app browser, so at any zoom other than 100% the screenshot attached to an annotation was offset and undersized. Scale the region by the guest's zoom factor before capturing. Closes #5690 Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import {
|
|
createBrowserCaptureService,
|
|
type BrowserCaptureGuest,
|
|
type BrowserCaptureImage,
|
|
} from "./browser-capture.js";
|
|
|
|
function image(dataUrl = "data:image/png;base64,capture"): BrowserCaptureImage {
|
|
return { isEmpty: () => false, toDataURL: () => dataUrl };
|
|
}
|
|
|
|
function harness(guest: BrowserCaptureGuest | null = null) {
|
|
const clipboard = { write: vi.fn(async () => undefined) };
|
|
const decodeImage = vi.fn(() => image("data:image/png;base64,clipboard"));
|
|
const warn = vi.fn();
|
|
return {
|
|
clipboard,
|
|
decodeImage,
|
|
warn,
|
|
service: createBrowserCaptureService({
|
|
findGuest: () => guest,
|
|
decodeImage,
|
|
clipboard,
|
|
warn,
|
|
}),
|
|
};
|
|
}
|
|
|
|
describe("browser capture service", () => {
|
|
it("validates and rounds guest-relative bounds before capture", async () => {
|
|
const capturePage = vi.fn(async () => image());
|
|
const { service } = harness({ isDestroyed: () => false, getZoomFactor: () => 1, capturePage });
|
|
|
|
await expect(
|
|
service.capture({
|
|
browserId: "browser-1",
|
|
hostWebContentsId: 42,
|
|
rect: { x: -2.4, y: 8.6, width: 20.2, height: 10.8 },
|
|
}),
|
|
).resolves.toBe("data:image/png;base64,capture");
|
|
expect(capturePage).toHaveBeenCalledWith({ x: 0, y: 9, width: 20, height: 11 });
|
|
});
|
|
|
|
it("captures the selected element when the page is zoomed", async () => {
|
|
const capturePage = vi.fn(async () => image());
|
|
const { service } = harness({
|
|
isDestroyed: () => false,
|
|
getZoomFactor: () => 1.2,
|
|
capturePage,
|
|
});
|
|
|
|
await service.capture({
|
|
browserId: "browser-1",
|
|
hostWebContentsId: 42,
|
|
rect: { x: 300, y: 200, width: 120, height: 60 },
|
|
});
|
|
expect(capturePage).toHaveBeenCalledWith({ x: 360, y: 240, width: 144, height: 72 });
|
|
});
|
|
|
|
it("rejects invalid or unavailable captures without touching the guest", async () => {
|
|
const capturePage = vi.fn(async () => image());
|
|
const { service } = harness({ isDestroyed: () => false, getZoomFactor: () => 1, capturePage });
|
|
|
|
await expect(
|
|
service.capture({ browserId: "browser-1", hostWebContentsId: 42, rect: { width: 0 } }),
|
|
).resolves.toBeNull();
|
|
expect(capturePage).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("writes text and a decoded image to the clipboard atomically", async () => {
|
|
const { service, clipboard } = harness();
|
|
await expect(
|
|
service.copy({ text: "button", imageDataUrl: "data:image/png;base64,value" }),
|
|
).resolves.toBe(true);
|
|
expect(clipboard.write).toHaveBeenCalledWith({
|
|
text: "button",
|
|
image: expect.any(Object),
|
|
});
|
|
});
|
|
|
|
it("keeps text-only clipboard writes", async () => {
|
|
const { service, clipboard } = harness();
|
|
|
|
await expect(service.copy({ text: "button" })).resolves.toBe(true);
|
|
expect(clipboard.write).toHaveBeenCalledWith({ text: "button", image: null });
|
|
});
|
|
});
|