Files
OpenFrontIO/tests/client/Platform.test.ts
T
Skigim f7598369ed refactor: consolidate platform detection across client components (#3325)
## 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
2026-03-02 10:12:48 -08:00

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);
});
});