diff --git a/COMMUNITY_POSTS.md b/COMMUNITY_POSTS.md index 0f1d5b5..01c734a 100644 --- a/COMMUNITY_POSTS.md +++ b/COMMUNITY_POSTS.md @@ -18,12 +18,13 @@ Sendefertiger Content-Pack mit aktuellen, verifizierten Links aus der Website-St - Release Status: https://www.typewhisper.com/en/release-status/ - Benchmark: https://www.typewhisper.com/en/benchmark/ - Changelog: https://www.typewhisper.com/en/changelog/ +- iOS: https://apps.apple.com/app/typewhisper/id6759319267 ## Product Status - macOS: stable - Windows: stable -- iOS: coming soon (pending Apple review) +- iOS: stable (App Store) ## German Reddit @@ -50,7 +51,7 @@ Discord: https://discord.gg/pUFR4a65SD Aktueller Plattform-Stand: macOS = stable Windows = stable -iOS = coming soon (pending Apple review) +iOS = stable: https://apps.apple.com/app/typewhisper/id6759319267 TypeWhisper ist inzwischen nicht nur eine Dictation-App, sondern eher eine Speech-to-Text-/Workflow-Plattform mit lokalen Engines, Add-ons, Profilen sowie CLI/API für Integrationen. Falls ihr in anderen passenden Communities unterwegs seid, etwa rund um macOS, AI-Tools, Accessibility, Productivity oder DevTools, könnt ihr die Links dort gern auch weiterposten. ``` @@ -96,7 +97,7 @@ For setup issues or quick workflow questions around TypeWhisper, these are the u ### 7. Platform Status ```text -Current TypeWhisper platform status is pretty straightforward: macOS and Windows are stable, while iOS is pending Apple review. Reference page: https://www.typewhisper.com/en/release-status/ +Current TypeWhisper platform status is straightforward: macOS, Windows, and iOS are stable. iOS: https://apps.apple.com/app/typewhisper/id6759319267 Reference page: https://www.typewhisper.com/en/release-status/ ``` ### 8. Productivity / macOS @@ -135,7 +136,7 @@ Support: https://www.typewhisper.com/en/support/ Current platform status: macOS = stable Windows = stable -iOS = coming soon (pending Apple review) +iOS = stable (App Store) If you know other builder or productivity communities where this would be relevant, feel free to repost it there. ``` @@ -155,7 +156,7 @@ Add-ons: https://www.typewhisper.com/en/addons/ Plugin SDK: https://www.typewhisper.com/en/addons/develop/ Support: https://www.typewhisper.com/en/support/ -Current release status is macOS and Windows stable, with iOS pending Apple review. +Current release status is macOS, Windows, and iOS stable. iOS: https://apps.apple.com/app/typewhisper/id6759319267 If this is useful elsewhere, reposts in other relevant communities are welcome. ``` @@ -185,7 +186,7 @@ https://discord.gg/pUFR4a65SD Current platform status: macOS stable Windows stable -iOS coming soon (pending Apple review) +iOS stable: https://apps.apple.com/app/typewhisper/id6759319267 TypeWhisper seems increasingly useful both as a dictation app and as a speech-to-text workflow platform, especially with profiles, add-ons, and local model options. If you're in other macOS, AI, productivity, accessibility, or devtool communities, feel free to repost this there too. ``` @@ -212,7 +213,7 @@ For anyone getting started or going deeper: Platform status: - macOS: stable - Windows: stable -- iOS: coming soon (pending Apple review) +- iOS: stable (https://apps.apple.com/app/typewhisper/id6759319267) If you're in other relevant communities, feel free to repost this there. ``` @@ -237,7 +238,7 @@ Discord: https://discord.gg/pUFR4a65SD Current platform status: macOS stable Windows stable -iOS coming soon (pending Apple review) +iOS stable: https://apps.apple.com/app/typewhisper/id6759319267 Feel free to forward this to other relevant teams or communities if useful. ``` diff --git a/DESIGN.md b/DESIGN.md index b632d7a..cf8e6ae 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -5,7 +5,7 @@ Always read this file before making visual changes. Do not deviate without expli ## Product Context -- **What this is:** Marketing site for TypeWhisper, a privacy-first dictation app (macOS and Windows stable, iOS pending Apple review). +- **What this is:** Marketing site for TypeWhisper, a privacy-first dictation app with stable macOS, Windows, and iOS editions. - **Who it's for:** Developers, writers, professionals; secondary: business/legal buyers. - **Positioning:** Free, local-first, open ecosystem (add-on marketplace + SDK), made in Germany. - **Project type:** Marketing site + docs, statically built with Astro 6 + Tailwind 4 + React islands, EN/DE. diff --git a/src/components/landing/hero.tsx b/src/components/landing/hero.tsx index 66d9d34..700e596 100644 --- a/src/components/landing/hero.tsx +++ b/src/components/landing/hero.tsx @@ -9,7 +9,7 @@ import { IOSLogo, } from "@/components/ui/platform-logos"; import { HeroDemo } from "@/components/landing/hero-demo"; -import { getPlatformDownloadTarget } from "@/lib/platform-download"; +import { getPlatformDownloadTarget, iosVersion } from "@/lib/platform-download"; import { useLandingPlatformSelection, type LandingPlatform, @@ -37,11 +37,10 @@ function shortVersion(version: string | null | undefined): string | null { return match ? `${match[1]}.${match[2]}` : null; } -// iOS is not public yet, so there is no public version to advertise. const versionByPlatform: Record = { mac: shortVersion(downloads.mac.version), windows: shortVersion(downloads.windows.version), - ios: null, + ios: shortVersion(iosVersion), }; const logoByPlatform: Record< @@ -95,7 +94,9 @@ export function Hero({ locale = "en" }: { locale?: Locale }) { {versionByPlatform[selectedPlatform] ? ` ${versionByPlatform[selectedPlatform]}` : ""} - + {t(locale, `hero.eyebrow.${selectedPlatform}`)}

diff --git a/src/components/landing/platform-download-menu.tsx b/src/components/landing/platform-download-menu.tsx index 475e6f4..55e8200 100644 --- a/src/components/landing/platform-download-menu.tsx +++ b/src/components/landing/platform-download-menu.tsx @@ -1,4 +1,4 @@ -import { ChevronDown, Clock3, Download, Monitor } from "lucide-react"; +import { ChevronDown, Download, Monitor, Smartphone } from "lucide-react"; import { getPlatformDownloadTarget } from "@/lib/platform-download"; import { t, type Locale } from "@/i18n/index"; @@ -11,9 +11,19 @@ export function PlatformDownloadMenu({ }) { const resolvedLabel = label ?? t(locale, "platformMenu.label"); const macDownload = getPlatformDownloadTarget("mac", locale, "landing"); - const windowsDownload = getPlatformDownloadTarget("windows", locale, "landing"); + const windowsDownload = getPlatformDownloadTarget( + "windows", + locale, + "landing", + ); + const iosDownload = getPlatformDownloadTarget("ios", locale, "landing"); - if (!macDownload.available || !windowsDownload.available) return null; + if ( + !macDownload.available || + !windowsDownload.available || + !iosDownload.available + ) + return null; return (
@@ -50,13 +60,20 @@ export function PlatformDownloadMenu({ {t(locale, "platformMenu.win")} - - + {t(locale, "platformMenu.ios")} - +
); diff --git a/src/components/landing/platforms-coming-soon.tsx b/src/components/landing/platforms-coming-soon.tsx index bc360ad..1cbd3a9 100644 --- a/src/components/landing/platforms-coming-soon.tsx +++ b/src/components/landing/platforms-coming-soon.tsx @@ -67,7 +67,7 @@ function getPlatforms(locale: Locale): PlatformInfo[] { description: t(locale, "platforms.ios.description"), status: t(locale, "platforms.ios.status"), statusClassName: - "bg-amber-500/10 text-amber-700 dark:text-amber-300", + "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300", download: getPlatformDownloadTarget("ios", locale, "landing"), docsPath: "/docs/ios", screenshot: screenshotPath(locale, "/screenshots/ios/04-history.webp"), @@ -144,7 +144,9 @@ export function AvailablePlatforms({ locale = "en" }: { locale?: Locale }) {
@@ -95,7 +114,9 @@ export default function DocsIOS({ locale = "en" }: { locale?: Locale }) { {isDe ? "Aktuelle App-Vorschau" : "Current App Preview"}

- {isDe ? "Der komplette Ablauf in 30 Sekunden." : "The complete flow in 30 seconds."} + {isDe + ? "Der komplette Ablauf in 30 Sekunden." + : "The complete flow in 30 seconds."}

{isDe @@ -104,11 +125,15 @@ export default function DocsIOS({ locale = "en" }: { locale?: Locale }) {

-
iPhone / iPad
+
+ iPhone / iPad +
iOS 18+
-
Apple Watch
+
+ Apple Watch +
watchOS 11+
@@ -137,11 +162,17 @@ export default function DocsIOS({ locale = "en" }: { locale?: Locale }) {
-
+

{isDe ? "Nicht nur eine Featureliste" : "More than a feature list"}

-

+

{isDe ? "Die vollständige iOS-Anleitung." : "The complete iOS guide."}

@@ -157,7 +188,9 @@ export default function DocsIOS({ locale = "en" }: { locale?: Locale }) { href={localePath(locale, `/docs/ios/${slug}`)} className="group grid grid-cols-[2.5rem_1fr_auto] items-center gap-3 border-b border-border py-4 last:border-b-0" > - {String(index + 1).padStart(2, "0")} + + {String(index + 1).padStart(2, "0")} + {getIosDocTitle(locale, slug)} @@ -167,9 +200,17 @@ export default function DocsIOS({ locale = "en" }: { locale?: Locale }) {

-
-

- {isDe ? "iPhone und iPad, aktuell abgebildet." : "iPhone and iPad, shown as they are now."} +
+

+ {isDe + ? "iPhone und iPad, aktuell abgebildet." + : "iPhone and iPad, shown as they are now."}

{isDe @@ -183,7 +224,10 @@ export default function DocsIOS({ locale = "en" }: { locale?: Locale }) { className="w-[14rem] flex-none snap-center overflow-hidden rounded-[2rem] border border-border bg-card p-1.5 shadow-xl shadow-black/10 sm:w-[16rem]" >

-
-

+
+

{isDe ? "Aufnehmen am Handgelenk." : "Capture from your wrist."}

@@ -212,9 +266,15 @@ export default function DocsIOS({ locale = "en" }: { locale?: Locale }) {

{watchScreenshots.map((screenshot) => ( -
+

- {isDe ? "Aktueller Veröffentlichungsstatus" : "Current release status"} + {isDe + ? "Aktueller Veröffentlichungsstatus" + : "Current release status"}

{isDe - ? "Version 1.0 befindet sich in der Vorbereitung für die öffentliche App-Store-Veröffentlichung. Diese Seite wird mit dem offiziellen Download-Link aktualisiert, sobald die App tatsächlich verfügbar ist." - : "Version 1.0 is being prepared for its public App Store release. This page will be updated with the official download link once the app is actually available."} + ? "Version 1.0 ist als stabiles Release für iPhone und iPad im App Store verfügbar. Die Apple-Watch-App ist enthalten." + : "Version 1.0 is available as a stable release for iPhone and iPad on the App Store. The Apple Watch app is included."}

+ + {isDe ? "App Store öffnen" : "Open the App Store"} +
@@ -241,8 +316,13 @@ export default function DocsIOS({ locale = "en" }: { locale?: Locale }) { {isDe ? "Direkter Support" : "Direct support"}

- {isDe ? "Für konkrete Fragen zur iOS-Version: " : "For specific questions about the iOS edition, email "} - + {isDe + ? "Für konkrete Fragen zur iOS-Version: " + : "For specific questions about the iOS edition, email "} + hello@typewhisper.com . diff --git a/src/pages/docs/ios/_guide.tsx b/src/pages/docs/ios/_guide.tsx index 60f8a3f..ef24c35 100644 --- a/src/pages/docs/ios/_guide.tsx +++ b/src/pages/docs/ios/_guide.tsx @@ -6,6 +6,7 @@ import { type IosDocSlug, } from "@/data/ios-docs"; import { localePath, screenshotPath, type Locale } from "@/i18n/index"; +import { getIosAppStoreUrl, iosVersion } from "@/lib/platform-download"; function imageClass(layout: "phone" | "tablet" | "wide" = "wide") { if (layout === "phone") { @@ -26,6 +27,7 @@ export default function DocsIOSGuide({ }) { const page = getIosDocPage(locale, slug); const isDe = locale === "de"; + const iosAppStoreUrl = getIosAppStoreUrl(locale); return (

@@ -42,7 +44,20 @@ export default function DocsIOSGuide({
iOS / iPadOS 18+ watchOS 11+ - {isDe ? "Version 1.0 in Vorbereitung" : "Version 1.0 in preparation"} + {isDe ? "Version 1.0 stabil" : "Version 1.0 stable"} + + {isDe ? "App Store öffnen" : "Open the App Store"} +
diff --git a/tests/download-routing.spec.ts b/tests/download-routing.spec.ts index 33c3eca..bc9b13a 100644 --- a/tests/download-routing.spec.ts +++ b/tests/download-routing.spec.ts @@ -1,6 +1,11 @@ import { expect, test } from "@playwright/test"; import { readFileSync } from "node:fs"; +const IOS_APP_STORE_URL_EN = + "https://apps.apple.com/us/app/typewhisper-app/id6759319267"; +const IOS_APP_STORE_URL_DE = + "https://apps.apple.com/de/app/typewhisper-app/id6759319267"; + type GeneratedDownloads = { mac: { url: string }; windows: { url: string }; @@ -52,7 +57,9 @@ const landingScenarios: LandingScenario[] = [ name: "iOS", userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.4 Mobile/15E148 Safari/604.1", - expectedLabel: "Not public yet", + expectedLabel: "Download on the App Store", + expectedHref: IOS_APP_STORE_URL_EN, + opensNewTab: true, }, ]; @@ -285,8 +292,8 @@ test.describe("release status download routing", () => { page.getByRole("link", { name: "Download GitHub installer" }), ).toHaveAttribute("href", downloads.windows.url); await expect( - page.getByText("Not in the App Store yet", { exact: true }), - ).toBeVisible(); + page.getByRole("link", { name: "Download on the App Store" }), + ).toHaveAttribute("href", IOS_APP_STORE_URL_EN); await expect(page.locator('a[href*="testflight.apple.com"]')).toHaveCount( 0, ); @@ -320,15 +327,15 @@ test.describe("release status download routing", () => { page.getByRole("link", { name: "GitHub-Installer herunterladen" }), ).toHaveAttribute("href", downloads.windows.url); await expect( - page.getByText("Noch nicht im App Store", { exact: true }), - ).toBeVisible(); + page.getByRole("link", { name: "Im App Store laden" }), + ).toHaveAttribute("href", IOS_APP_STORE_URL_DE); await expect(page.locator('a[href*="testflight.apple.com"]')).toHaveCount( 0, ); }); }); -test("public iOS pages show release-preparation status without beta links", async ({ +test("public iOS pages expose the stable App Store release without beta links", async ({ page, }) => { for (const path of ["/en/", "/en/docs", "/en/docs/ios", "/en/support"]) { @@ -338,16 +345,49 @@ test("public iOS pages show release-preparation status without beta links", asyn ); } + await page.goto("/en/"); + const footerAppStoreLink = page.locator( + 'footer a[data-download-target="ios_app_store"]', + ); + await expect(footerAppStoreLink).toHaveAttribute( + "data-tracking-placement", + "footer", + ); + await page.goto("/en/docs/ios"); await expect( - page.getByText("App Store release in preparation", { exact: true }), - ).toBeVisible(); + page.getByRole("link", { name: "Download on the App Store" }), + ).toHaveAttribute("href", IOS_APP_STORE_URL_EN); await expect( - page.getByText("Version 1.0 in preparation", { exact: true }).first(), + page.getByText("Version 1.0 stable", { exact: true }), ).toBeVisible(); + + await page.goto("/en/support"); + await expect( + page.getByRole("link", { name: "Email iOS support" }), + ).toHaveAttribute("href", "mailto:hello@typewhisper.com"); + const supportAppStoreLink = page.getByRole("link", { + name: "Open the App Store", + }); + await expect(supportAppStoreLink).toHaveAttribute( + "href", + IOS_APP_STORE_URL_EN, + ); + await expect(supportAppStoreLink).toHaveAttribute( + "data-download-target", + "ios_app_store", + ); + await expect(supportAppStoreLink).toHaveAttribute( + "data-download-version", + "1.0", + ); + await expect(supportAppStoreLink).toHaveAttribute( + "data-tracking-placement", + "support", + ); }); -test.describe("iOS coming-soon media", () => { +test.describe("iOS App Store media", () => { test("English landing uses the localized App Preview", async ({ page, request, @@ -361,7 +401,18 @@ test.describe("iOS coming-soon media", () => { await iosTab.click(); await expect(iosTab).toHaveAttribute("aria-selected", "true"); - await expect(page.getByTestId("landing-hero-download")).toBeDisabled(); + await expect(page.getByTestId("landing-hero-download")).toHaveAttribute( + "href", + IOS_APP_STORE_URL_EN, + ); + await expect(page.getByTestId("landing-hero-download")).toHaveAttribute( + "data-download-target", + "ios_app_store", + ); + await expect(page.getByTestId("landing-hero-download")).toHaveAttribute( + "data-download-version", + "1.0", + ); await expect( page.locator('source[src="/ios-app-preview-de.mp4"]'), ).toHaveCount(0); @@ -397,6 +448,10 @@ test.describe("iOS coming-soon media", () => { const iosTab = page.getByTestId("landing-hero-tab-ios"); await iosTab.click(); await expect(iosTab).toHaveAttribute("aria-selected", "true"); + await expect(page.getByTestId("landing-hero-download")).toHaveAttribute( + "href", + IOS_APP_STORE_URL_DE, + ); const howItWorks = page.getByTestId("how-it-works"); await howItWorks.scrollIntoViewIfNeeded(); @@ -421,7 +476,18 @@ test.describe("iOS coming-soon media", () => { await expect( page.getByRole("heading", { level: 1, name: "iOS" }), ).toBeVisible(); - await expect(page.locator('a[href*="apps.apple.com"]')).toHaveCount(0); + const appStoreUrl = + locale === "de" ? IOS_APP_STORE_URL_DE : IOS_APP_STORE_URL_EN; + const appStoreLinks = page.locator(`a[href="${appStoreUrl}"]`); + await expect(appStoreLinks.first()).toBeVisible(); + await expect(appStoreLinks.first()).toHaveAttribute( + "data-download-target", + "ios_app_store", + ); + await expect(appStoreLinks.first()).toHaveAttribute( + "data-download-version", + "1.0", + ); await expect(page.locator('a[href*="testflight.apple.com"]')).toHaveCount( 0, ); @@ -436,7 +502,9 @@ test.describe("iOS coming-soon media", () => { const pngPath = `/screenshots/${locale}/ios/watch/${name}.png`; const webpPath = `/screenshots/${locale}/ios/watch/${name}.webp`; await expect(page.locator(`img[src="${pngPath}"]`)).toBeVisible(); - await expect(page.locator(`source[srcset="${webpPath}"]`)).toHaveCount(1); + await expect(page.locator(`source[srcset="${webpPath}"]`)).toHaveCount( + 1, + ); expect((await request.get(pngPath)).ok()).toBeTruthy(); expect((await request.get(webpPath)).ok()).toBeTruthy(); } diff --git a/tests/ios-docs.spec.ts b/tests/ios-docs.spec.ts index aca64eb..9f157c6 100644 --- a/tests/ios-docs.spec.ts +++ b/tests/ios-docs.spec.ts @@ -1,35 +1,50 @@ import { expect, test } from "@playwright/test"; import { getIosDocTitle, iosDocSlugs } from "../src/data/ios-docs"; -const guidePages = iosDocSlugs.map((slug) => [ - slug, - getIosDocTitle("en", slug), - getIosDocTitle("de", slug), -] as const); +const guidePages = iosDocSlugs.map( + (slug) => + [slug, getIosDocTitle("en", slug), getIosDocTitle("de", slug)] as const, +); test.describe("detailed iOS documentation", () => { - test("overview links every guide and uses current Studio media", async ({ page }) => { + test("overview links every guide and uses current Studio media", async ({ + page, + }) => { await page.goto("/en/docs/ios"); for (const [slug, englishTitle] of guidePages) { await expect( - page.locator(`a[href="/en/docs/ios/${slug}"]`).filter({ hasText: englishTitle }).first(), + page + .locator(`a[href="/en/docs/ios/${slug}"]`) + .filter({ hasText: englishTitle }) + .first(), ).toBeVisible(); } - await expect(page.locator('source[src="/ios-app-preview-en.mp4"]')).toHaveCount(1); + await expect( + page.locator('source[src="/ios-app-preview-en.mp4"]'), + ).toHaveCount(1); await expect( page.locator('img[src="/screenshots/en/ios/ipad/03-inbox.png"]'), ).toBeVisible(); await expect( page.locator('source[srcset="/screenshots/en/ios/ipad/03-inbox.webp"]'), ).toHaveCount(1); - await expect(page.locator('a[href*="testflight.apple.com"]')).toHaveCount(0); - await expect(page.locator('a[href*="apps.apple.com"]')).toHaveCount(0); + await expect(page.locator('a[href*="testflight.apple.com"]')).toHaveCount( + 0, + ); + await expect( + page.getByRole("link", { name: "Download on the App Store" }), + ).toHaveAttribute( + "href", + "https://apps.apple.com/us/app/typewhisper-app/id6759319267", + ); }); for (const locale of ["en", "de"] as const) { - test(`${locale} guide routes render with the iOS sidebar`, async ({ page }) => { + test(`${locale} guide routes render with the iOS sidebar`, async ({ + page, + }) => { for (const [slug, englishTitle, germanTitle] of guidePages) { await page.goto(`/${locale}/docs/ios/${slug}`); await expect( @@ -41,38 +56,102 @@ test.describe("detailed iOS documentation", () => { await expect( page.locator(`nav a[href="/${locale}/docs/ios/${slug}"]`), ).toHaveClass(/border-primary/); + await expect( + page.getByText(locale === "de" ? "Stabil" : "Stable", { + exact: true, + }).first(), + ).toBeVisible(); } }); } - test("keyboard guide explains Full Access and the iOS return flow", async ({ page }) => { + test("keyboard guide explains Full Access and the iOS return flow", async ({ + page, + }) => { await page.goto("/en/docs/ios/dictation-and-keyboard"); - await expect(page.getByText("Settings > General > Keyboard > Keyboards > Add New Keyboard")).toBeVisible(); - await expect(page.getByText(/Normal typing works without it/)).toBeVisible(); - await expect(page.getByText(/iOS keyboard extensions cannot capture microphone audio directly/)).toBeVisible(); + await expect( + page.getByText( + "Settings > General > Keyboard > Keyboards > Add New Keyboard", + ), + ).toBeVisible(); + await expect( + page.getByText(/Normal typing works without it/), + ).toBeVisible(); + await expect( + page.getByText( + /iOS keyboard extensions cannot capture microphone audio directly/, + ), + ).toBeVisible(); await expect(page.getByText(/previous-app link/).first()).toBeVisible(); }); - test("Inbox, Shortcuts, privacy, and Premium behavior are documented", async ({ page }) => { + test("installation and troubleshooting describe the public App Store path", async ({ + page, + }) => { + await page.goto("/en/docs/ios/installation"); + await expect(page.getByText(/available from the App Store/)).toBeVisible(); + await expect( + page.getByRole("link", { name: "Open the App Store" }), + ).toHaveAttribute( + "href", + "https://apps.apple.com/us/app/typewhisper-app/id6759319267", + ); + + await page.goto("/en/docs/ios/troubleshooting"); + await expect( + page.getByRole("heading", { + name: "The App Store download does not work", + }), + ).toBeVisible(); + await expect( + page.getByText(/official App Store product page/), + ).toBeVisible(); + }); + + test("Inbox, Shortcuts, privacy, and Premium behavior are documented", async ({ + page, + }) => { await page.goto("/en/docs/ios/files-history-and-inbox"); - await expect(page.getByText(/Quick Dictations, Apple Watch recordings, calendar drafts/)).toBeVisible(); - await expect(page.locator('img[src="/screenshots/en/ios/ipad/03-inbox.png"]')).toBeVisible(); + await expect( + page.getByText( + /Quick Dictations, Apple Watch recordings, calendar drafts/, + ), + ).toBeVisible(); + await expect( + page.locator('img[src="/screenshots/en/ios/ipad/03-inbox.png"]'), + ).toBeVisible(); await page.goto("/en/docs/ios/watch-and-shortcuts"); - await expect(page.getByText(/Get Last Transcription returns/)).toBeVisible(); - await expect(page.getByText(/assign that Shortcut to the Action Button/)).toBeVisible(); + await expect( + page.getByText(/Get Last Transcription returns/), + ).toBeVisible(); + await expect( + page.getByText(/assign that Shortcut to the Action Button/), + ).toBeVisible(); await page.goto("/en/docs/ios/privacy-and-premium"); - await expect(page.getByText(/Normal keyboard typing works without Full Access/)).toBeVisible(); - await expect(page.getByText(/History and Inbox text and metadata/)).toBeVisible(); - await expect(page.getByText(/does not pass through the TypeWhisper entitlement service/)).toBeVisible(); + await expect( + page.getByText(/Normal keyboard typing works without Full Access/), + ).toBeVisible(); + await expect( + page.getByText(/History and Inbox text and metadata/), + ).toBeVisible(); + await expect( + page.getByText( + /does not pass through the TypeWhisper entitlement service/, + ), + ).toBeVisible(); }); - test("snippet placeholders expose localized list semantics", async ({ page }) => { + test("snippet placeholders expose localized list semantics", async ({ + page, + }) => { await page.goto("/en/docs/ios/dictionary-and-snippets"); - const placeholders = page.getByRole("list", { name: "Snippet placeholders" }); + const placeholders = page.getByRole("list", { + name: "Snippet placeholders", + }); await expect(placeholders).toBeVisible(); await expect(placeholders.getByRole("listitem")).toHaveCount(5); }); diff --git a/tests/ux-quick-wins.spec.ts b/tests/ux-quick-wins.spec.ts index 2172f85..0250a89 100644 --- a/tests/ux-quick-wins.spec.ts +++ b/tests/ux-quick-wins.spec.ts @@ -60,6 +60,39 @@ test.describe("docs platform logos", () => { await expect(page.locator('[data-platform-logo="macos"]').first()).toBeVisible(); await expect(page.locator('[data-platform-logo="ios"]').first()).toBeVisible(); }); + + for (const locale of ["en", "de"] as const) { + test(`${locale} docs release badges include platform versions`, async ({ + page, + }) => { + const windowsBadge = locale === "de" ? "1.0 Stabil" : "1.0 Stable"; + + await page.goto(`/${locale}/docs`); + await expect(page.locator("main")).toContainText("Windows 1.0.9"); + await expect( + page.locator(`a[href="/${locale}/docs/mac"]`).getByText("1.6", { + exact: true, + }), + ).toBeVisible(); + await expect( + page + .locator(`a[href="/${locale}/docs/windows"]`) + .getByText(windowsBadge, { exact: true }), + ).toBeVisible(); + await expect( + page + .locator(`a[href="/${locale}/docs/ios"]`) + .getByText(locale === "de" ? "1.0 Stabil" : "1.0 Stable", { + exact: true, + }), + ).toBeVisible(); + + await page.goto(`/${locale}/docs/windows`); + await expect( + page.getByText(windowsBadge, { exact: true }).last(), + ).toBeVisible(); + }); + } }); test.describe("brand logos", () => {