feat(platforms): dual Mac arch downloads with better detection (v1.0.7)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ttrpg-player-landing",
|
"name": "ttrpg-player-landing",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.6",
|
"version": "1.0.7",
|
||||||
"description": "Marketing landing for TTRPG Player (НРИ Плеер)",
|
"description": "Marketing landing for TTRPG Player (НРИ Плеер)",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -28,6 +28,22 @@
|
|||||||
|
|
||||||
.downloadBtn {
|
.downloadBtn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
white-space: normal;
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
.downloadStack {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.archHint {
|
||||||
|
margin: 2px 0 0;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: var(--color-text-dim);
|
||||||
}
|
}
|
||||||
|
|
||||||
.note {
|
.note {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { useMemo } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
import { DOWNLOAD_LINKS, PLATFORM_IDS } from "../../data/siteContent";
|
import { DOWNLOAD_LINKS, PLATFORM_IDS } from "../../data/siteContent";
|
||||||
import { useLocale } from "../../i18n/LocaleProvider";
|
import { useLocale } from "../../i18n/LocaleProvider";
|
||||||
import {
|
import {
|
||||||
|
type CpuArch,
|
||||||
|
detectCpuArch,
|
||||||
|
detectCpuArchAsync,
|
||||||
detectOs,
|
detectOs,
|
||||||
resolveDownloadHref,
|
resolveDownloadHref,
|
||||||
resolvePlatformDownloadHref,
|
resolvePlatformDownloadHref,
|
||||||
@@ -24,19 +27,54 @@ function readNavigatorHints(): { userAgent: string; maxTouchPoints: number } {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function macArchLabel(
|
||||||
|
arch: "arm64" | "x64",
|
||||||
|
labels: { arm64: string; x64: string; recommended: string },
|
||||||
|
preferred: CpuArch,
|
||||||
|
): string {
|
||||||
|
const base = arch === "arm64" ? labels.arm64 : labels.x64;
|
||||||
|
if (preferred === arch) return `${base} · ${labels.recommended}`;
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
export function Platforms() {
|
export function Platforms() {
|
||||||
const { copy } = useLocale();
|
const { copy } = useLocale();
|
||||||
|
|
||||||
const hints = useMemo(() => readNavigatorHints(), []);
|
const hints = useMemo(() => readNavigatorHints(), []);
|
||||||
|
const [cpuArch, setCpuArch] = useState<CpuArch>(() =>
|
||||||
|
detectCpuArch(hints.userAgent, hints.maxTouchPoints),
|
||||||
|
);
|
||||||
|
|
||||||
const { osDownloadHref, detectedOs } = useMemo(() => {
|
useEffect(() => {
|
||||||
const os = detectOs(hints.userAgent);
|
let cancelled = false;
|
||||||
return {
|
void detectCpuArchAsync(hints).then((arch) => {
|
||||||
osDownloadHref: resolveDownloadHref(os, DOWNLOAD_LINKS, hints),
|
if (!cancelled) setCpuArch(arch);
|
||||||
detectedOs: os,
|
});
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [hints]);
|
}, [hints]);
|
||||||
|
|
||||||
|
const archHints = useMemo(
|
||||||
|
() => ({
|
||||||
|
userAgent: hints.userAgent,
|
||||||
|
maxTouchPoints: hints.maxTouchPoints,
|
||||||
|
uaArchitecture:
|
||||||
|
cpuArch === "arm64" ? "arm" : cpuArch === "x64" ? "x86" : null,
|
||||||
|
}),
|
||||||
|
[hints, cpuArch],
|
||||||
|
);
|
||||||
|
|
||||||
|
const detectedOs = useMemo(
|
||||||
|
() => detectOs(hints.userAgent),
|
||||||
|
[hints.userAgent],
|
||||||
|
);
|
||||||
|
|
||||||
|
const osDownloadHref = useMemo(
|
||||||
|
() => resolveDownloadHref(detectedOs, DOWNLOAD_LINKS, archHints),
|
||||||
|
[detectedOs, archHints],
|
||||||
|
);
|
||||||
|
|
||||||
const trackOsDownload = () => {
|
const trackOsDownload = () => {
|
||||||
const platform =
|
const platform =
|
||||||
detectedOs === "windows" ||
|
detectedOs === "windows" ||
|
||||||
@@ -52,12 +90,15 @@ export function Platforms() {
|
|||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
PLATFORM_IDS.map((id) => [
|
PLATFORM_IDS.map((id) => [
|
||||||
id,
|
id,
|
||||||
resolvePlatformDownloadHref(id, DOWNLOAD_LINKS, hints),
|
resolvePlatformDownloadHref(id, DOWNLOAD_LINKS, archHints),
|
||||||
]),
|
]),
|
||||||
) as Record<(typeof PLATFORM_IDS)[number], string>,
|
) as Record<(typeof PLATFORM_IDS)[number], string>,
|
||||||
[hints],
|
[archHints],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const macPreferredArch: CpuArch =
|
||||||
|
detectedOs === "macos" ? cpuArch : "unknown";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section id="platforms" title={copy.platforms.title}>
|
<Section id="platforms" title={copy.platforms.title}>
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
@@ -68,6 +109,44 @@ export function Platforms() {
|
|||||||
<PlatformLogo platform={id} />
|
<PlatformLogo platform={id} />
|
||||||
<h3 className={styles.cardTitle}>{platform.title}</h3>
|
<h3 className={styles.cardTitle}>{platform.title}</h3>
|
||||||
<p className={styles.cardText}>{platform.description}</p>
|
<p className={styles.cardText}>{platform.description}</p>
|
||||||
|
|
||||||
|
{id === "macos" ? (
|
||||||
|
<div className={styles.downloadStack}>
|
||||||
|
<Button
|
||||||
|
href={DOWNLOAD_LINKS.macos.arm64}
|
||||||
|
variant={
|
||||||
|
macPreferredArch === "arm64" ? "secondary" : "ghost"
|
||||||
|
}
|
||||||
|
className={styles.downloadBtn}
|
||||||
|
onClick={() => {
|
||||||
|
trackDownload("macos");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{macArchLabel(
|
||||||
|
"arm64",
|
||||||
|
copy.platforms.macosArch,
|
||||||
|
macPreferredArch,
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
href={DOWNLOAD_LINKS.macos.x64}
|
||||||
|
variant={macPreferredArch === "x64" ? "secondary" : "ghost"}
|
||||||
|
className={styles.downloadBtn}
|
||||||
|
onClick={() => {
|
||||||
|
trackDownload("macos");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{macArchLabel(
|
||||||
|
"x64",
|
||||||
|
copy.platforms.macosArch,
|
||||||
|
macPreferredArch,
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<p className={styles.archHint}>
|
||||||
|
{copy.platforms.macosArch.hint}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<Button
|
<Button
|
||||||
href={platformDownloadHrefs[id]}
|
href={platformDownloadHrefs[id]}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -78,6 +157,7 @@ export function Platforms() {
|
|||||||
>
|
>
|
||||||
{copy.cta.download}
|
{copy.cta.download}
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
+16
-2
@@ -168,13 +168,20 @@ const ru = {
|
|||||||
},
|
},
|
||||||
macos: {
|
macos: {
|
||||||
title: "macOS",
|
title: "macOS",
|
||||||
description: "Intel (x64) и Apple Silicon (M1/M2/M3). DMG.",
|
description:
|
||||||
|
"Отдельные DMG для Intel и Apple Silicon (M1/M2/M3). Выберите архитектуру ниже.",
|
||||||
},
|
},
|
||||||
linux: {
|
linux: {
|
||||||
title: "Linux",
|
title: "Linux",
|
||||||
description: "x64 и ARM64. AppImage.",
|
description: "x64 и ARM64. AppImage.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
macosArch: {
|
||||||
|
arm64: "Apple Silicon",
|
||||||
|
x64: "Intel",
|
||||||
|
recommended: "рекомендуется",
|
||||||
|
hint: " → Об этом Mac → Чип / Процессор",
|
||||||
|
},
|
||||||
note: "Автообновления для лицензированных пользователей · Интерфейс на русском и английском · Работает офлайн",
|
note: "Автообновления для лицензированных пользователей · Интерфейс на русском и английском · Работает офлайн",
|
||||||
distributionDisclaimer:
|
distributionDisclaimer:
|
||||||
"Установочные файлы не подписаны сертификатом издателя и предоставляются «как есть» (as is), без каких‑либо явных или подразумеваемых гарантий. Разработчик не несёт ответственности за ущерб, связанный с загрузкой, установкой или использованием приложения. Устанавливая программу, вы принимаете все риски и ответственность за последствия на своей стороне.",
|
"Установочные файлы не подписаны сертификатом издателя и предоставляются «как есть» (as is), без каких‑либо явных или подразумеваемых гарантий. Разработчик не несёт ответственности за ущерб, связанный с загрузкой, установкой или использованием приложения. Устанавливая программу, вы принимаете все риски и ответственность за последствия на своей стороне.",
|
||||||
@@ -438,13 +445,20 @@ const en: SiteCopy = {
|
|||||||
},
|
},
|
||||||
macos: {
|
macos: {
|
||||||
title: "macOS",
|
title: "macOS",
|
||||||
description: "Intel (x64) and Apple Silicon (M1/M2/M3). DMG.",
|
description:
|
||||||
|
"Separate DMGs for Intel and Apple Silicon (M1/M2/M3). Pick your architecture below.",
|
||||||
},
|
},
|
||||||
linux: {
|
linux: {
|
||||||
title: "Linux",
|
title: "Linux",
|
||||||
description: "x64 and ARM64. AppImage.",
|
description: "x64 and ARM64. AppImage.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
macosArch: {
|
||||||
|
arm64: "Apple Silicon",
|
||||||
|
x64: "Intel",
|
||||||
|
recommended: "recommended",
|
||||||
|
hint: " → About This Mac → Chip / Processor",
|
||||||
|
},
|
||||||
note: "Auto-updates for licensed users · Russian and English UI · Works offline",
|
note: "Auto-updates for licensed users · Russian and English UI · Works offline",
|
||||||
distributionDisclaimer:
|
distributionDisclaimer:
|
||||||
"Installers are not code-signed by the publisher and are provided “as is”, without express or implied warranties. The developer is not liable for any damage arising from downloading, installing or using the application. By installing the software, you assume all risks and responsibility on your side.",
|
"Installers are not code-signed by the publisher and are provided “as is”, without express or implied warranties. The developer is not liable for any damage arising from downloading, installing or using the application. By installing the software, you assume all risks and responsibility on your side.",
|
||||||
|
|||||||
+96
-16
@@ -3,9 +3,17 @@ import type { DownloadLinks, PlatformId } from "../data/siteContent";
|
|||||||
export type OsId = "windows" | "macos" | "linux" | "unknown";
|
export type OsId = "windows" | "macos" | "linux" | "unknown";
|
||||||
export type CpuArch = "x64" | "arm64" | "unknown";
|
export type CpuArch = "x64" | "arm64" | "unknown";
|
||||||
|
|
||||||
type NavigatorHints = {
|
export type NavigatorHints = {
|
||||||
userAgent: string;
|
userAgent: string;
|
||||||
maxTouchPoints?: number;
|
maxTouchPoints?: number;
|
||||||
|
/** From Client Hints `architecture` when available (e.g. "arm", "x86") */
|
||||||
|
uaArchitecture?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type NavigatorUAData = {
|
||||||
|
getHighEntropyValues: (
|
||||||
|
hints: string[],
|
||||||
|
) => Promise<{ architecture?: string; bitness?: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function detectOs(userAgent: string): OsId {
|
export function detectOs(userAgent: string): OsId {
|
||||||
@@ -16,8 +24,39 @@ export function detectOs(userAgent: string): OsId {
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Best-effort CPU arch from UA; macOS Apple Silicon often masquerades as MacIntel */
|
/** Map Client Hints / UA-CH architecture token to CpuArch */
|
||||||
export function detectCpuArch(userAgent: string, maxTouchPoints = 0): CpuArch {
|
export function parseUaDataArchitecture(
|
||||||
|
architecture: string | null | undefined,
|
||||||
|
): CpuArch {
|
||||||
|
if (!architecture) return "unknown";
|
||||||
|
const arch = architecture.toLowerCase();
|
||||||
|
if (arch === "arm" || arch.includes("arm64") || arch.includes("aarch64")) {
|
||||||
|
return "arm64";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
arch === "x86" ||
|
||||||
|
arch.includes("x86_64") ||
|
||||||
|
arch.includes("amd64") ||
|
||||||
|
arch.includes("x64")
|
||||||
|
) {
|
||||||
|
return "x64";
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Best-effort CPU arch from UA / Client Hints.
|
||||||
|
* macOS Apple Silicon often masquerades as "Intel Mac OS X" in the UA string —
|
||||||
|
* prefer uaArchitecture or maxTouchPoints; otherwise return unknown (do not guess Intel).
|
||||||
|
*/
|
||||||
|
export function detectCpuArch(
|
||||||
|
userAgent: string,
|
||||||
|
maxTouchPoints = 0,
|
||||||
|
uaArchitecture?: string | null,
|
||||||
|
): CpuArch {
|
||||||
|
const fromHints = parseUaDataArchitecture(uaArchitecture);
|
||||||
|
if (fromHints !== "unknown") return fromHints;
|
||||||
|
|
||||||
const ua = userAgent.toLowerCase();
|
const ua = userAgent.toLowerCase();
|
||||||
if (ua.includes("aarch64") || ua.includes("arm64")) return "arm64";
|
if (ua.includes("aarch64") || ua.includes("arm64")) return "arm64";
|
||||||
if (ua.includes("x86_64") || ua.includes("amd64") || ua.includes("win64")) {
|
if (ua.includes("x86_64") || ua.includes("amd64") || ua.includes("win64")) {
|
||||||
@@ -26,26 +65,65 @@ export function detectCpuArch(userAgent: string, maxTouchPoints = 0): CpuArch {
|
|||||||
if (/armv\d+/u.test(ua)) return "arm64";
|
if (/armv\d+/u.test(ua)) return "arm64";
|
||||||
|
|
||||||
if (ua.includes("mac")) {
|
if (ua.includes("mac")) {
|
||||||
return maxTouchPoints > 1 ? "arm64" : "x64";
|
// Safari on Apple Silicon often reports maxTouchPoints > 1
|
||||||
|
if (maxTouchPoints > 1) return "arm64";
|
||||||
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resolve arch using navigator.userAgentData when the browser exposes it */
|
||||||
|
export async function detectCpuArchAsync(
|
||||||
|
hints: NavigatorHints,
|
||||||
|
): Promise<CpuArch> {
|
||||||
|
const sync = detectCpuArch(
|
||||||
|
hints.userAgent,
|
||||||
|
hints.maxTouchPoints ?? 0,
|
||||||
|
hints.uaArchitecture,
|
||||||
|
);
|
||||||
|
if (sync !== "unknown") return sync;
|
||||||
|
|
||||||
|
if (typeof navigator === "undefined") return sync;
|
||||||
|
|
||||||
|
const uaData = (navigator as Navigator & { userAgentData?: NavigatorUAData })
|
||||||
|
.userAgentData;
|
||||||
|
if (!uaData?.getHighEntropyValues) return sync;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const values = await uaData.getHighEntropyValues(["architecture"]);
|
||||||
|
const fromUaData = parseUaDataArchitecture(values.architecture);
|
||||||
|
if (fromUaData !== "unknown") return fromUaData;
|
||||||
|
} catch {
|
||||||
|
// Permission / unsupported — keep sync result
|
||||||
|
}
|
||||||
|
|
||||||
|
return sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
function macDownloadHref(arch: CpuArch, links: DownloadLinks): string {
|
||||||
|
// Prefer Apple Silicon when unsure — majority of current Macs; Intel remains selectable in UI
|
||||||
|
return arch === "x64" ? links.macos.x64 : links.macos.arm64;
|
||||||
|
}
|
||||||
|
|
||||||
|
function linuxDownloadHref(arch: CpuArch, links: DownloadLinks): string {
|
||||||
|
return arch === "arm64" ? links.linux.arm64 : links.linux.x64;
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveDownloadHref(
|
export function resolveDownloadHref(
|
||||||
os: OsId,
|
os: OsId,
|
||||||
links: DownloadLinks,
|
links: DownloadLinks,
|
||||||
hints: NavigatorHints,
|
hints: NavigatorHints,
|
||||||
): string {
|
): string {
|
||||||
const arch = detectCpuArch(hints.userAgent, hints.maxTouchPoints ?? 0);
|
const arch = detectCpuArch(
|
||||||
|
hints.userAgent,
|
||||||
|
hints.maxTouchPoints ?? 0,
|
||||||
|
hints.uaArchitecture,
|
||||||
|
);
|
||||||
|
|
||||||
if (os === "windows") return links.windows;
|
if (os === "windows") return links.windows;
|
||||||
if (os === "macos") {
|
if (os === "macos") return macDownloadHref(arch, links);
|
||||||
return arch === "x64" ? links.macos.x64 : links.macos.arm64;
|
if (os === "linux") return linuxDownloadHref(arch, links);
|
||||||
}
|
|
||||||
if (os === "linux") {
|
|
||||||
return arch === "arm64" ? links.linux.arm64 : links.linux.x64;
|
|
||||||
}
|
|
||||||
return links.windows;
|
return links.windows;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,15 +136,17 @@ export function resolvePlatformDownloadHref(
|
|||||||
if (platform === "windows") return links.windows;
|
if (platform === "windows") return links.windows;
|
||||||
|
|
||||||
const os = detectOs(hints.userAgent);
|
const os = detectOs(hints.userAgent);
|
||||||
const arch = detectCpuArch(hints.userAgent, hints.maxTouchPoints ?? 0);
|
const arch = detectCpuArch(
|
||||||
|
hints.userAgent,
|
||||||
|
hints.maxTouchPoints ?? 0,
|
||||||
|
hints.uaArchitecture,
|
||||||
|
);
|
||||||
|
|
||||||
if (platform === "macos") {
|
if (platform === "macos") {
|
||||||
if (os === "macos") {
|
if (os === "macos") return macDownloadHref(arch, links);
|
||||||
return arch === "x64" ? links.macos.x64 : links.macos.arm64;
|
|
||||||
}
|
|
||||||
return links.macos.arm64;
|
return links.macos.arm64;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (os === "linux" && arch === "arm64") return links.linux.arm64;
|
if (os === "linux") return linuxDownloadHref(arch, links);
|
||||||
return links.linux.x64;
|
return links.linux.x64;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { MESSAGES, inferLocaleFromLanguages } from "./i18n/messages";
|
|||||||
import {
|
import {
|
||||||
detectCpuArch,
|
detectCpuArch,
|
||||||
detectOs,
|
detectOs,
|
||||||
|
parseUaDataArchitecture,
|
||||||
resolveDownloadHref,
|
resolveDownloadHref,
|
||||||
resolvePlatformDownloadHref,
|
resolvePlatformDownloadHref,
|
||||||
} from "./lib/detectOs";
|
} from "./lib/detectOs";
|
||||||
@@ -121,6 +122,8 @@ describe("i18n messages", () => {
|
|||||||
assert.equal(MESSAGES.en.cta.download, "Download");
|
assert.equal(MESSAGES.en.cta.download, "Download");
|
||||||
assert.equal(MESSAGES.ru.cta.buy, "Купить");
|
assert.equal(MESSAGES.ru.cta.buy, "Купить");
|
||||||
assert.equal(MESSAGES.en.cta.buy, "Buy");
|
assert.equal(MESSAGES.en.cta.buy, "Buy");
|
||||||
|
assert.equal(MESSAGES.ru.platforms.macosArch.arm64, "Apple Silicon");
|
||||||
|
assert.equal(MESSAGES.en.platforms.macosArch.x64, "Intel");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -156,6 +159,32 @@ describe("detectOs", () => {
|
|||||||
assert.equal(detectCpuArch("Mozilla/5.0 (X11; Linux aarch64)"), "arm64");
|
assert.equal(detectCpuArch("Mozilla/5.0 (X11; Linux aarch64)"), "arm64");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("parses Client Hints architecture tokens", () => {
|
||||||
|
assert.equal(parseUaDataArchitecture("arm"), "arm64");
|
||||||
|
assert.equal(parseUaDataArchitecture("x86"), "x64");
|
||||||
|
assert.equal(parseUaDataArchitecture("arm64"), "arm64");
|
||||||
|
assert.equal(parseUaDataArchitecture(undefined), "unknown");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not guess Intel on opaque Mac user agents", () => {
|
||||||
|
assert.equal(
|
||||||
|
detectCpuArch("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)", 0),
|
||||||
|
"unknown",
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
detectCpuArch("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)", 5),
|
||||||
|
"arm64",
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
detectCpuArch(
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
|
||||||
|
0,
|
||||||
|
"arm",
|
||||||
|
),
|
||||||
|
"arm64",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("resolves download href by OS", () => {
|
it("resolves download href by OS", () => {
|
||||||
const hints = { userAgent: "Mozilla/5.0 (Windows NT 10.0)" };
|
const hints = { userAgent: "Mozilla/5.0 (Windows NT 10.0)" };
|
||||||
assert.equal(
|
assert.equal(
|
||||||
@@ -166,9 +195,17 @@ describe("detectOs", () => {
|
|||||||
resolveDownloadHref("macos", DOWNLOAD_LINKS, {
|
resolveDownloadHref("macos", DOWNLOAD_LINKS, {
|
||||||
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X)",
|
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X)",
|
||||||
maxTouchPoints: 0,
|
maxTouchPoints: 0,
|
||||||
|
uaArchitecture: "x86",
|
||||||
}),
|
}),
|
||||||
DOWNLOAD_LINKS.macos.x64,
|
DOWNLOAD_LINKS.macos.x64,
|
||||||
);
|
);
|
||||||
|
assert.equal(
|
||||||
|
resolveDownloadHref("macos", DOWNLOAD_LINKS, {
|
||||||
|
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X)",
|
||||||
|
maxTouchPoints: 0,
|
||||||
|
}),
|
||||||
|
DOWNLOAD_LINKS.macos.arm64,
|
||||||
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
resolveDownloadHref("linux", DOWNLOAD_LINKS, {
|
resolveDownloadHref("linux", DOWNLOAD_LINKS, {
|
||||||
userAgent: "Mozilla/5.0 (X11; Linux aarch64)",
|
userAgent: "Mozilla/5.0 (X11; Linux aarch64)",
|
||||||
|
|||||||
Reference in New Issue
Block a user