Add TTRPG Player marketing landing site.

React/Vite landing with RU/EN i18n, live USD pricing, download links to updates.mailib.ru, and donate.stream checkout.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-06-28 11:58:31 +08:00
commit b0ab6cc729
70 changed files with 9151 additions and 0 deletions
+185
View File
@@ -0,0 +1,185 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
DOWNLOAD_ARTIFACTS,
DOWNLOAD_LINKS,
FAQ_IDS,
FEATURE_VARIANTS,
NAV_LINK_KEYS,
NAV_HREFS,
PRICING_TIER_IDS,
RECOMMENDED_PRICING_TIER,
REQUIRED_SECTION_IDS,
SITE_LINKS,
UPDATE_FEED_BASE,
} from "./data/siteContent";
import {
SCREENSHOT_SRC,
getEffectsScreenshotDisplaySize,
} from "./data/siteAssets";
import { MESSAGES, inferLocaleFromLanguages } from "./i18n/messages";
import {
detectCpuArch,
detectOs,
resolveDownloadHref,
resolvePlatformDownloadHref,
} from "./lib/detectOs";
describe("siteContent", () => {
it("exposes all required section anchor ids", () => {
for (const id of REQUIRED_SECTION_IDS) {
const inNav = NAV_LINK_KEYS.some((key) => NAV_HREFS[key] === `#${id}`);
assert.equal(inNav || id.length > 0, true, `missing section #${id}`);
}
});
it("defines four feature variants", () => {
assert.deepEqual(FEATURE_VARIANTS, [
"editor",
"control",
"presentation",
"atmosphere",
]);
});
it("defines three pricing tiers with one recommended", () => {
assert.equal(PRICING_TIER_IDS.length, 3);
assert.equal(RECOMMENDED_PRICING_TIER, "master");
});
it("includes five FAQ entries", () => {
assert.equal(FAQ_IDS.length, 5);
});
it("points download buttons to update feed artifacts", () => {
assert.equal(UPDATE_FEED_BASE, "https://updates.mailib.ru/");
assert.equal(
DOWNLOAD_LINKS.windows,
`${UPDATE_FEED_BASE}${DOWNLOAD_ARTIFACTS.windows}`,
);
assert.equal(
DOWNLOAD_LINKS.macos.arm64,
`${UPDATE_FEED_BASE}${DOWNLOAD_ARTIFACTS.macos.arm64}`,
);
assert.equal(
DOWNLOAD_LINKS.linux.x64,
`${UPDATE_FEED_BASE}${DOWNLOAD_ARTIFACTS.linux.x64}`,
);
});
it("wires download anchor and external buy link", () => {
assert.equal(SITE_LINKS.download, "#platforms");
assert.equal(SITE_LINKS.buy, "https://donate.stream/ttrpgplayer");
});
it("maps each feature variant to a screenshot asset", () => {
for (const variant of FEATURE_VARIANTS) {
assert.match(SCREENSHOT_SRC[variant], /^\/images\/screenshot-.+\.png$/u);
}
assert.equal(SCREENSHOT_SRC.atmosphere, "/images/screenshot-effects.png");
});
it("shows effects screenshot at 70% of intrinsic pixel size", () => {
const size = getEffectsScreenshotDisplaySize();
assert.equal(size.width, 206);
assert.equal(size.height, 220);
});
});
describe("i18n messages", () => {
it("has ru and en locales without DND key mentions", () => {
for (const locale of ["ru", "en"] as const) {
const json = JSON.stringify(MESSAGES[locale]);
assert.doesNotMatch(json, /DND-/u);
assert.doesNotMatch(json, /\.dnd\.zip/u);
assert.equal(Object.keys(MESSAGES[locale].faq.items).length, 5);
}
});
it("documents download and buy labels in both locales", () => {
assert.equal(MESSAGES.ru.cta.download, "Скачать");
assert.equal(MESSAGES.en.cta.download, "Download");
assert.equal(MESSAGES.ru.cta.buy, "Купить");
assert.equal(MESSAGES.en.cta.buy, "Buy");
});
});
describe("inferLocaleFromLanguages", () => {
it("uses ru only for Russian browser languages", () => {
assert.equal(inferLocaleFromLanguages(["ru-RU"]), "ru");
assert.equal(inferLocaleFromLanguages(["ru"]), "ru");
assert.equal(inferLocaleFromLanguages(["ru-BY", "en-US"]), "ru");
});
it("defaults to en for non-Russian browser languages", () => {
assert.equal(inferLocaleFromLanguages(["en-US"]), "en");
assert.equal(inferLocaleFromLanguages(["de-DE"]), "en");
assert.equal(inferLocaleFromLanguages(["uk-UA"]), "en");
assert.equal(inferLocaleFromLanguages([]), "en");
});
});
describe("detectOs", () => {
it("detects Windows from user agent", () => {
assert.equal(detectOs("Mozilla/5.0 (Windows NT 10.0)"), "windows");
});
it("detects macOS from user agent", () => {
assert.equal(detectOs("Mozilla/5.0 (Macintosh; Intel Mac OS X)"), "macos");
});
it("detects Linux from user agent", () => {
assert.equal(detectOs("Mozilla/5.0 (X11; Linux x86_64)"), "linux");
});
it("detects Linux arm64 from user agent", () => {
assert.equal(detectCpuArch("Mozilla/5.0 (X11; Linux aarch64)"), "arm64");
});
it("resolves download href by OS", () => {
const hints = { userAgent: "Mozilla/5.0 (Windows NT 10.0)" };
assert.equal(
resolveDownloadHref("windows", DOWNLOAD_LINKS, hints),
DOWNLOAD_LINKS.windows,
);
assert.equal(
resolveDownloadHref("macos", DOWNLOAD_LINKS, {
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X)",
maxTouchPoints: 0,
}),
DOWNLOAD_LINKS.macos.x64,
);
assert.equal(
resolveDownloadHref("linux", DOWNLOAD_LINKS, {
userAgent: "Mozilla/5.0 (X11; Linux aarch64)",
}),
DOWNLOAD_LINKS.linux.arm64,
);
assert.equal(
resolveDownloadHref("unknown", DOWNLOAD_LINKS, hints),
DOWNLOAD_LINKS.windows,
);
});
it("resolves per-platform card href", () => {
assert.equal(
resolvePlatformDownloadHref("windows", DOWNLOAD_LINKS, {
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X)",
}),
DOWNLOAD_LINKS.windows,
);
assert.equal(
resolvePlatformDownloadHref("macos", DOWNLOAD_LINKS, {
userAgent: "Mozilla/5.0 (Windows NT 10.0)",
}),
DOWNLOAD_LINKS.macos.arm64,
);
assert.equal(
resolvePlatformDownloadHref("linux", DOWNLOAD_LINKS, {
userAgent: "Mozilla/5.0 (X11; Linux x86_64)",
}),
DOWNLOAD_LINKS.linux.x64,
);
});
});