Release Device Tools v0.1.0

This commit is contained in:
2026-09-01 14:22:42 +02:00
commit 06f697e180
57 changed files with 9775 additions and 0 deletions
+182
View File
@@ -0,0 +1,182 @@
import { describe, expect, it, vi } from "vitest";
import {
collectPassiveCapabilities,
createRedactedReport,
exportReportCsv,
exportReportJson,
runProbe,
type Capability,
type DeviceEnvironment,
type ProbeResult,
} from "../../src/core/device";
function environment(
navigatorOverrides: Record<string, unknown> = {},
): DeviceEnvironment {
const fakeWindow = {
innerWidth: 1234,
innerHeight: 777,
devicePixelRatio: 2.25,
top: null,
visualViewport: { width: 1200, height: 700 },
matchMedia: (query: string) => ({ matches: query.includes("fine") }),
WebGLRenderingContext: function WebGLRenderingContext() {},
indexedDB: {},
caches: {},
} as unknown as Window;
Object.defineProperty(fakeWindow, "top", { value: fakeWindow });
const fakeDocument = {
fullscreenEnabled: true,
pictureInPictureEnabled: true,
createElement: (name: string) =>
name === "video" || name === "audio"
? {
canPlayType: (mime: string) =>
mime.includes("opus") ? "probably" : "",
}
: {},
} as unknown as Document;
return {
window: fakeWindow,
document: fakeDocument,
navigator: {
onLine: true,
maxTouchPoints: 5,
mediaDevices: {
getSupportedConstraints: () => ({
width: true,
echoCancellation: true,
}),
},
...navigatorOverrides,
} as unknown as DeviceEnvironment["navigator"],
screen: {
width: 2560,
height: 1440,
availWidth: 2500,
availHeight: 1400,
colorDepth: 24,
orientation: { type: "landscape-primary" },
} as Screen,
secureContext: true,
isolated: false,
};
}
describe("passive device inventory", () => {
it("reports display, input, constraints, codecs and API presence without probing", () => {
const capabilities = collectPassiveCapabilities(environment());
expect(capabilities.find((item) => item.id === "viewport")?.value).toBe(
"1234 × 777 CSS px",
);
expect(capabilities.find((item) => item.id === "touch")?.state).toBe(
"available",
);
expect(
capabilities.find((item) => item.id === "media-constraints")?.value,
).toContain("echoCancellation");
expect(capabilities.find((item) => item.id === "codec-opus")?.value).toBe(
"Probably",
);
expect(capabilities.find((item) => item.id === "camera-api")?.state).toBe(
"unavailable",
);
});
});
describe("explicit probes", () => {
it("reads storage locally and preserves only buckets in a report", async () => {
const result = await runProbe(
"storage",
environment({
storage: {
estimate: async () => ({ usage: 500_000_000, quota: 2_000_000_000 }),
},
}),
);
expect(result.state).toBe("complete");
expect(result.details[0]?.value).toContain("MiB");
const report = createRedactedReport([], [result]);
const serialized = exportReportJson(report);
expect(serialized).not.toContain("476.84");
expect(serialized).toContain("25-to-49%");
});
it("stops every user-media track immediately", async () => {
const stop = vi.fn();
const result = await runProbe(
"camera",
environment({
mediaDevices: {
getUserMedia: async () => ({ getTracks: () => [{ stop }, { stop }] }),
},
}),
);
expect(result.state).toBe("complete");
expect(stop).toHaveBeenCalledTimes(2);
});
it("queries permission state without treating unsupported names as failure", async () => {
const query = vi.fn(async ({ name }: { name: string }) => {
if (name === "camera") throw new TypeError("not supported");
return { state: "prompt" };
});
const result = await runProbe(
"permissions",
environment({ permissions: { query } }),
);
expect(result.state).toBe("complete");
expect(result.details.find((item) => item.label === "camera")?.value).toBe(
"unsupported",
);
expect(query).toHaveBeenCalled();
});
});
describe("redacted exports", () => {
it("never emits exact high-entropy capability values", () => {
const capabilities: Capability[] = [
{
id: "viewport",
group: "Display",
label: "Viewport",
state: "available",
value: "1234 × 777 CSS px",
privacy: "high",
explanation: "",
},
{
id: "dpr",
group: "Display",
label: "DPR",
state: "available",
value: "2.25",
privacy: "high",
explanation: "",
},
{
id: "touch",
group: "Input",
label: "Touch",
state: "available",
value: "5 maximum point(s)",
privacy: "medium",
explanation: "",
},
];
const probe: ProbeResult = {
id: "media-devices",
state: "complete",
summary: "done",
details: [{ label: "videoinput", value: "7", redacted: "many" }],
};
const report = createRedactedReport(capabilities, [probe]);
const json = exportReportJson(report);
expect(json).not.toContain("1234");
expect(json).not.toContain("2.25");
expect(json).toContain("medium");
expect(json).toContain("multi-touch");
expect(exportReportCsv(report)).toContain("capability,viewport");
expect(report.policy.join(" ")).toContain("No stable identifier");
});
});