MeshChatX/tests/frontend/RNodeCapabilities.test.js
2026-07-26 10:28:14 -05:00

169 lines
8 KiB
JavaScript

import { describe, it, expect } from "vitest";
import {
detectCapabilities,
pickDefaultTransport,
transportSuggestionKeys,
TRANSPORT_SERIAL,
TRANSPORT_BLUETOOTH,
TRANSPORT_WIFI,
} from "@/js/rnode/Capabilities.js";
const mkEnv = (overrides = {}) => ({
isSecureContext: true,
navigator: { userAgent: "Mozilla/5.0", ...(overrides.navigator || {}) },
...overrides,
});
describe("Capabilities.detectCapabilities", () => {
it("reports native serial when navigator.serial is present", () => {
const env = mkEnv({ navigator: { userAgent: "x", serial: {} } });
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_SERIAL].available).toBe(true);
expect(caps.transports[TRANSPORT_SERIAL].kind).toBe("native");
});
it("reports polyfill serial when usb + serial polyfill module are available", () => {
const env = mkEnv({ navigator: { userAgent: "x", usb: {} }, serial: {} });
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_SERIAL].available).toBe(true);
expect(caps.transports[TRANSPORT_SERIAL].polyfilled).toBe(true);
});
it("flags missing polyfill when only usb is exposed", () => {
const env = mkEnv({ navigator: { userAgent: "x", usb: {} } });
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_SERIAL].available).toBe(false);
expect(caps.transports[TRANSPORT_SERIAL].reason).toBe("polyfill_not_loaded");
});
it("reports android_webview_no_serial on Android without USB", () => {
const env = mkEnv({ navigator: { userAgent: "Linux; Android 11; SomePhone" } });
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_SERIAL].available).toBe(false);
expect(caps.transports[TRANSPORT_SERIAL].reason).toBe("android_webview_no_serial");
expect(caps.platform.isAndroid).toBe(true);
});
it("reports android-native serial when MeshChatXAndroid has native flasher", () => {
const env = mkEnv({
navigator: { userAgent: "Linux; Android 14" },
MeshChatXAndroid: { hasNativeRNodeFlasher: () => true },
});
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_SERIAL].available).toBe(true);
expect(caps.transports[TRANSPORT_SERIAL].kind).toBe("android-native-activity");
});
it("reports bluetooth permission required on MeshChatXAndroid without grant", () => {
const env = mkEnv({
navigator: { userAgent: "Android" },
MeshChatXAndroid: { hasBluetoothPermissions: () => false },
});
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_BLUETOOTH].available).toBe(false);
expect(caps.transports[TRANSPORT_BLUETOOTH].reason).toBe("android_bluetooth_permission_required");
});
it("reports android_bridge_no_web_bluetooth when MeshChatXAndroid already granted BT", () => {
const env = mkEnv({
navigator: { userAgent: "Android" },
MeshChatXAndroid: { hasBluetoothPermissions: () => true },
});
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_BLUETOOTH].available).toBe(false);
expect(caps.transports[TRANSPORT_BLUETOOTH].reason).toBe("android_bridge_no_web_bluetooth");
});
it("reports bluetooth available when navigator.bluetooth is present", () => {
const env = mkEnv({ navigator: { userAgent: "x", bluetooth: {} } });
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_BLUETOOTH].available).toBe(true);
});
it("flags insecure_context for bluetooth when not in secure context", () => {
const env = mkEnv({ isSecureContext: false, navigator: { userAgent: "x" } });
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_BLUETOOTH].available).toBe(false);
expect(caps.transports[TRANSPORT_BLUETOOTH].reason).toBe("insecure_context");
});
it("detects Brave with Web Bluetooth disabled as brave_flag_disabled", () => {
const env = mkEnv({
isSecureContext: true,
navigator: { userAgent: "Mozilla/5.0 Brave/1.0" },
});
const caps = detectCapabilities({ env });
expect(caps.platform.isBrave).toBe(true);
expect(caps.transports[TRANSPORT_BLUETOOTH].available).toBe(false);
expect(caps.transports[TRANSPORT_BLUETOOTH].reason).toBe("brave_flag_disabled");
});
it("detects Brave via navigator.brave even without Brave in UA", () => {
const env = mkEnv({
isSecureContext: true,
navigator: { userAgent: "Mozilla/5.0 Chrome/120", brave: {} },
});
const caps = detectCapabilities({ env });
expect(caps.platform.isBrave).toBe(true);
expect(caps.transports[TRANSPORT_BLUETOOTH].reason).toBe("brave_flag_disabled");
});
it("prefers insecure_context over brave_flag_disabled", () => {
const env = mkEnv({
isSecureContext: false,
navigator: { userAgent: "Mozilla/5.0 Brave/1.0" },
});
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_BLUETOOTH].reason).toBe("insecure_context");
});
it("always exposes wifi transport as available", () => {
const env = mkEnv();
const caps = detectCapabilities({ env });
expect(caps.transports[TRANSPORT_WIFI].available).toBe(true);
});
});
describe("Capabilities.pickDefaultTransport", () => {
it("prefers serial when available", () => {
const caps = detectCapabilities({ env: mkEnv({ navigator: { userAgent: "x", serial: {}, bluetooth: {} } }) });
expect(pickDefaultTransport(caps)).toBe(TRANSPORT_SERIAL);
});
it("falls back to bluetooth when serial unavailable", () => {
const caps = detectCapabilities({ env: mkEnv({ navigator: { userAgent: "x", bluetooth: {} } }) });
expect(pickDefaultTransport(caps)).toBe(TRANSPORT_BLUETOOTH);
});
it("falls back to wifi when nothing else", () => {
const caps = detectCapabilities({ env: mkEnv() });
expect(pickDefaultTransport(caps)).toBe(TRANSPORT_WIFI);
});
});
describe("Capabilities.transportSuggestionKeys", () => {
it("returns empty list when transport is available", () => {
const caps = detectCapabilities({ env: mkEnv({ navigator: { userAgent: "x", serial: {} } }) });
expect(transportSuggestionKeys(caps, TRANSPORT_SERIAL)).toEqual([]);
});
it("returns reason key + android hint for serial on android", () => {
const env = mkEnv({ navigator: { userAgent: "Android" } });
const caps = detectCapabilities({ env });
const keys = transportSuggestionKeys(caps, TRANSPORT_SERIAL);
expect(keys).toContain("tools.rnode_flasher.support.serial.android_webview_no_serial");
expect(keys).toContain("tools.rnode_flasher.support.serial.android_use_chrome");
});
it("includes https hint for bluetooth when not secure", () => {
const env = mkEnv({ isSecureContext: false, navigator: { userAgent: "x" } });
const caps = detectCapabilities({ env });
const keys = transportSuggestionKeys(caps, TRANSPORT_BLUETOOTH);
expect(keys).toContain("tools.rnode_flasher.support.bluetooth.insecure_context");
expect(keys).toContain("tools.rnode_flasher.support.bluetooth.requires_https");
});
it("includes Brave flag guidance when Web Bluetooth is disabled", () => {
const env = mkEnv({ navigator: { userAgent: "Mozilla/5.0 Brave/1.0" } });
const caps = detectCapabilities({ env });
const keys = transportSuggestionKeys(caps, TRANSPORT_BLUETOOTH);
expect(keys).toContain("tools.rnode_flasher.support.bluetooth.brave_flag_disabled");
expect(keys).toContain("tools.rnode_flasher.support.bluetooth.brave_enable_flag");
expect(keys).toContain("tools.rnode_flasher.support.bluetooth.brave_recheck");
});
});