mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 10:53:31 +00:00
f7598369ed
## Description: This PR consolidates ad hoc platform/environment/viewport detection into a single shared utility. It is scoped to this refactor only, and serves as groundwork for the mobile-focused feature work planned for the v31 milestone. ### What changed - Introduced a shared `Platform` utility centralising: - OS detection (with `userAgentData` + UA fallback) - Electron environment detection - Viewport breakpoint helpers (`isMobileWidth`, `isTabletWidth`, `isDesktopWidth`) - Replaced duplicated inline checks across client files with the shared API. - Normalised Mac detection to derive from the consolidated OS logic rather than a separate regex. ### Why - Multiple client files each independently ran `navigator.userAgent` regexes or copy-pasted `isElectron` logic — this unifies all of that. - Puts a stable, tested abstraction in place before v31 mobile work lands, so mobile feature branches have a consistent surface to build against. ## Please complete the following: - [x] I have added screenshots for all UI updates (N/A: refactor only, no visible UI changes) - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file (N/A: no new user-facing strings) - [x] I have added relevant tests to the test directory (N/A: refactor only) - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: skigim
135 lines
3.9 KiB
TypeScript
135 lines
3.9 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
type NavigatorOverride = {
|
|
userAgent: string;
|
|
userAgentData?: { platform?: string };
|
|
maxTouchPoints?: number;
|
|
};
|
|
|
|
const setInnerWidth = (value: number) => {
|
|
Object.defineProperty(window, "innerWidth", {
|
|
configurable: true,
|
|
writable: true,
|
|
value,
|
|
});
|
|
};
|
|
|
|
const loadPlatform = async ({
|
|
userAgent,
|
|
userAgentData,
|
|
maxTouchPoints,
|
|
}: NavigatorOverride) => {
|
|
vi.resetModules();
|
|
vi.stubGlobal("navigator", {
|
|
userAgent,
|
|
userAgentData,
|
|
maxTouchPoints,
|
|
});
|
|
const { Platform } = await import("../../src/client/Platform");
|
|
return Platform;
|
|
};
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("Platform", () => {
|
|
it("detects iOS before macOS for iPhone-like user agents", async () => {
|
|
const platform = await loadPlatform({
|
|
userAgent:
|
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1",
|
|
});
|
|
|
|
expect(platform.os).toBe("iOS");
|
|
expect(platform.isIOS).toBe(true);
|
|
expect(platform.isMac).toBe(false);
|
|
});
|
|
|
|
it("detects macOS for Macintosh user agents", async () => {
|
|
const platform = await loadPlatform({
|
|
userAgent:
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
|
|
});
|
|
|
|
expect(platform.os).toBe("macOS");
|
|
expect(platform.isMac).toBe(true);
|
|
expect(platform.isIOS).toBe(false);
|
|
});
|
|
|
|
it("detects iOS for iPad desktop-mode user agents with touch support", async () => {
|
|
const platform = await loadPlatform({
|
|
userAgent:
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15",
|
|
maxTouchPoints: 5,
|
|
});
|
|
|
|
expect(platform.os).toBe("iOS");
|
|
expect(platform.isIOS).toBe(true);
|
|
expect(platform.isMac).toBe(false);
|
|
});
|
|
|
|
it("uses userAgentData platform when available", async () => {
|
|
const platform = await loadPlatform({
|
|
userAgent:
|
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15",
|
|
userAgentData: { platform: "Android" },
|
|
});
|
|
|
|
expect(platform.os).toBe("Android");
|
|
expect(platform.isAndroid).toBe(true);
|
|
});
|
|
|
|
it("normalizes non-canonical userAgentData platform values", async () => {
|
|
const macPlatform = await loadPlatform({
|
|
userAgent: "Mozilla/5.0",
|
|
userAgentData: { platform: "Macintosh" },
|
|
});
|
|
|
|
expect(macPlatform.os).toBe("macOS");
|
|
expect(macPlatform.isMac).toBe(true);
|
|
|
|
const chromeOsPlatform = await loadPlatform({
|
|
userAgent: "Mozilla/5.0",
|
|
userAgentData: { platform: "Chrome OS" },
|
|
});
|
|
|
|
expect(chromeOsPlatform.os).toBe("Linux");
|
|
expect(chromeOsPlatform.isLinux).toBe(true);
|
|
|
|
const unknownPlatform = await loadPlatform({
|
|
userAgent: "Mozilla/5.0",
|
|
userAgentData: { platform: "PlayStation" },
|
|
});
|
|
|
|
expect(unknownPlatform.os).toBe("Unknown");
|
|
expect(unknownPlatform.isMac).toBe(false);
|
|
expect(unknownPlatform.isWindows).toBe(false);
|
|
expect(unknownPlatform.isIOS).toBe(false);
|
|
expect(unknownPlatform.isAndroid).toBe(false);
|
|
expect(unknownPlatform.isLinux).toBe(false);
|
|
});
|
|
|
|
it("reports viewport breakpoint helpers from window.innerWidth", async () => {
|
|
const platform = await loadPlatform({
|
|
userAgent:
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15",
|
|
});
|
|
|
|
setInnerWidth(767);
|
|
expect(platform.isMobileWidth).toBe(true);
|
|
expect(platform.isTabletWidth).toBe(false);
|
|
expect(platform.isDesktopWidth).toBe(false);
|
|
|
|
setInnerWidth(768);
|
|
expect(platform.isMobileWidth).toBe(false);
|
|
expect(platform.isTabletWidth).toBe(true);
|
|
expect(platform.isDesktopWidth).toBe(false);
|
|
|
|
setInnerWidth(1024);
|
|
expect(platform.isMobileWidth).toBe(false);
|
|
expect(platform.isTabletWidth).toBe(false);
|
|
expect(platform.isDesktopWidth).toBe(true);
|
|
});
|
|
});
|