mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
174 lines
4.7 KiB
TypeScript
174 lines
4.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { coverage, detectBlockingLayer } from "../layers";
|
|
import type { Viewport, VomNode } from "../types";
|
|
|
|
const VP: Viewport = { width: 1000, height: 800 };
|
|
|
|
function node(p: Partial<VomNode> & { id: number }): VomNode {
|
|
const { id, parentId, tag, rect, paintOrder, position, pointerEvents, ...rest } = p;
|
|
|
|
return {
|
|
id,
|
|
parentId: parentId ?? null,
|
|
tag: tag ?? "div",
|
|
rect: rect ?? null,
|
|
paintOrder: paintOrder ?? 0,
|
|
position: position ?? "static",
|
|
pointerEvents: pointerEvents ?? "auto",
|
|
...rest,
|
|
};
|
|
}
|
|
|
|
describe("coverage", () => {
|
|
it("returns the clamped viewport-overlap fraction", () => {
|
|
expect(coverage({ x: 0, y: 0, w: 1000, h: 800 }, VP)).toBeCloseTo(1);
|
|
expect(coverage({ x: 0, y: 0, w: 500, h: 800 }, VP)).toBeCloseTo(0.5);
|
|
expect(coverage({ x: -100, y: 0, w: 200, h: 800 }, VP)).toBeCloseTo(0.1);
|
|
});
|
|
|
|
it("returns 0 for null rect, invalid viewport, or off-viewport rect", () => {
|
|
expect(coverage(null, VP)).toBe(0);
|
|
expect(coverage({ x: 0, y: 0, w: 100, h: 100 }, { width: 0, height: 800 })).toBe(0);
|
|
expect(coverage({ x: 2000, y: 0, w: 100, h: 100 }, VP)).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("detectBlockingLayer", () => {
|
|
it("detects a CSS blocker and includes the whole top paint-order band", () => {
|
|
const layer = detectBlockingLayer(
|
|
[
|
|
node({ id: 1, tag: "body", rect: { x: 0, y: 0, w: 1000, h: 4000 }, paintOrder: 0 }),
|
|
node({
|
|
id: 2,
|
|
parentId: 1,
|
|
rect: { x: 0, y: 0, w: 1000, h: 800 },
|
|
paintOrder: 30,
|
|
position: "fixed",
|
|
}),
|
|
node({
|
|
id: 3,
|
|
parentId: 1,
|
|
role: "dialog",
|
|
name: "提示",
|
|
tag: "dialog",
|
|
rect: { x: 300, y: 250, w: 400, h: 300 },
|
|
paintOrder: 40,
|
|
position: "fixed",
|
|
}),
|
|
node({
|
|
id: 4,
|
|
parentId: 3,
|
|
role: "button",
|
|
name: "关闭",
|
|
tag: "button",
|
|
rect: { x: 640, y: 260, w: 40, h: 40 },
|
|
paintOrder: 41,
|
|
}),
|
|
],
|
|
VP,
|
|
);
|
|
|
|
expect(layer).not.toBeNull();
|
|
expect(layer?.rootId).toBe(2);
|
|
expect(layer?.kind).toBe("modal");
|
|
expect(layer?.coverage).toBeCloseTo(1);
|
|
expect([...(layer?.members ?? [])].sort((a, b) => a - b)).toEqual([2, 3, 4]);
|
|
});
|
|
|
|
it("classifies a near-full blocker without modal signals or controls as a mask", () => {
|
|
const layer = detectBlockingLayer(
|
|
[
|
|
node({ id: 1, tag: "body", rect: { x: 0, y: 0, w: 1000, h: 800 } }),
|
|
node({
|
|
id: 2,
|
|
parentId: 1,
|
|
rect: { x: 0, y: 0, w: 1000, h: 800 },
|
|
paintOrder: 10,
|
|
position: "fixed",
|
|
}),
|
|
],
|
|
VP,
|
|
);
|
|
|
|
expect(layer?.kind).toBe("mask");
|
|
});
|
|
|
|
it("treats explicit modal nodes as modal even below the generic coverage threshold", () => {
|
|
const layer = detectBlockingLayer(
|
|
[
|
|
node({ id: 1, tag: "body", rect: { x: 0, y: 0, w: 1000, h: 800 } }),
|
|
node({
|
|
id: 2,
|
|
parentId: 1,
|
|
tag: "dialog",
|
|
role: "dialog",
|
|
modal: true,
|
|
rect: { x: 300, y: 200, w: 400, h: 300 },
|
|
paintOrder: 90,
|
|
position: "fixed",
|
|
}),
|
|
],
|
|
VP,
|
|
);
|
|
|
|
expect(layer?.rootId).toBe(2);
|
|
expect(layer?.kind).toBe("modal");
|
|
});
|
|
|
|
it("detects modal features regardless of role or tag casing", () => {
|
|
const layer = detectBlockingLayer(
|
|
[
|
|
node({ id: 1, tag: "body", rect: { x: 0, y: 0, w: 1000, h: 800 } }),
|
|
node({
|
|
id: 2,
|
|
parentId: 1,
|
|
tag: "DIV",
|
|
role: "Dialog",
|
|
rect: { x: 300, y: 200, w: 400, h: 300 },
|
|
paintOrder: 90,
|
|
position: "fixed",
|
|
}),
|
|
],
|
|
VP,
|
|
);
|
|
|
|
expect(layer?.rootId).toBe(2);
|
|
expect(layer?.kind).toBe("modal");
|
|
});
|
|
|
|
it("ignores small toasts and pointer-events:none covers", () => {
|
|
expect(
|
|
detectBlockingLayer(
|
|
[
|
|
node({ id: 1, tag: "body", rect: { x: 0, y: 0, w: 1000, h: 800 } }),
|
|
node({
|
|
id: 2,
|
|
parentId: 1,
|
|
rect: { x: 800, y: 720, w: 180, h: 60 },
|
|
paintOrder: 99,
|
|
position: "fixed",
|
|
}),
|
|
],
|
|
VP,
|
|
),
|
|
).toBeNull();
|
|
|
|
expect(
|
|
detectBlockingLayer(
|
|
[
|
|
node({ id: 1, tag: "body", rect: { x: 0, y: 0, w: 1000, h: 800 } }),
|
|
node({
|
|
id: 2,
|
|
parentId: 1,
|
|
rect: { x: 0, y: 0, w: 1000, h: 800 },
|
|
paintOrder: 40,
|
|
position: "fixed",
|
|
pointerEvents: "none",
|
|
}),
|
|
],
|
|
VP,
|
|
),
|
|
).toBeNull();
|
|
});
|
|
});
|