Kokoro-FastAPI ships English, Spanish, French, Hindi, Italian, Japanese, Portuguese and Chinese voices only. The German presets pointed at voice ids no Kokoro server has, so German users either got English voices or picked a voice that failed to speak. The local voice now also lists the German voices whose Piper model is installed (PIPER_BASE_URL) and speaks them through Piper's /synthesize as WAV. Thorsten, the clearest model, is the German default. Mobile plays WAV as well as MP3, and the E2E voice stub stands in for Piper. Claude-Session: https://claude.ai/code/session_01KWEAjTw26eRf3ZXTXfRjmD Co-authored-by: Claude <noreply@anthropic.com>
91 lines
4.3 KiB
TypeScript
91 lines
4.3 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import { completeOnboarding, rpc, signup } from "./helpers";
|
|
|
|
test("onboarding names the first Oo and shows it in chat", async ({ page }) => {
|
|
const stamp = Date.now();
|
|
await signup(page, `name-oo-${stamp}@rakazo.test`, "password12", "Namer");
|
|
await page.waitForURL(/\/onboarding/, { timeout: 20_000 });
|
|
await page.getByRole("button", { name: /^(Continue|Weiter)$/i }).click();
|
|
await expect(page.getByTestId("onboarding-name-step")).toBeVisible();
|
|
await page.getByTestId("onboarding-oo-name-input").fill("Luna");
|
|
await page
|
|
.getByTestId("onboarding-name-step")
|
|
.getByRole("button", { name: /^(Continue|Weiter)$/i })
|
|
.click();
|
|
await page.getByRole("button", { name: /^(Skip|Überspringen ·)/i }).click();
|
|
await page.getByRole("button", { name: /^(Skip|Überspringen)$/i }).click();
|
|
await expect(page).toHaveURL(/\/app\/g\/[^/]+/, { timeout: 30_000 });
|
|
await expect(
|
|
page.getByTestId("bot-settings-trigger").getByText("Luna", { exact: true }),
|
|
).toBeVisible();
|
|
expect((await rpc<{ name: string }>(page, "bots/ensureMain", {})).name).toBe("Luna");
|
|
});
|
|
|
|
test("German UI keeps Deutsch on language skip label", async ({ page }) => {
|
|
await page.addInitScript(() => {
|
|
Object.defineProperty(navigator, "language", { value: "de-DE", configurable: true });
|
|
localStorage.setItem("rakazo.uiLocale", "de");
|
|
});
|
|
const stamp = Date.now();
|
|
await signup(page, `de-skip-${stamp}@rakazo.test`, "password12", "Deutsch");
|
|
await page.getByRole("button", { name: /^(Continue|Weiter)$/i }).click();
|
|
await page.getByRole("button", { name: /^(Skip|Überspringen)$/i }).click();
|
|
await expect(
|
|
page.getByRole("heading", { name: /Wähle deine Sprache|Choose your language/i }),
|
|
).toBeVisible();
|
|
await expect(page.getByRole("button", { name: /Überspringen · Deutsch/i })).toBeVisible();
|
|
});
|
|
|
|
test("German voice step lists German voices after choosing Deutsch", async ({ page }) => {
|
|
await page.addInitScript(() => {
|
|
Object.defineProperty(navigator, "language", { value: "de-DE", configurable: true });
|
|
localStorage.setItem("rakazo.uiLocale", "de");
|
|
});
|
|
const stamp = Date.now();
|
|
await signup(page, `de-voice-${stamp}@rakazo.test`, "password12", "Stimme");
|
|
await page.getByRole("button", { name: /^(Continue|Weiter)$/i }).click();
|
|
await page.getByRole("button", { name: /^(Skip|Überspringen)$/i }).click();
|
|
await page.getByRole("button", { name: "Deutsch", exact: true }).click();
|
|
await expect(page.getByText(/Loading voices|Stimmen werden geladen/i)).toHaveCount(0, {
|
|
timeout: 30_000,
|
|
});
|
|
await expect(page.getByTestId("onboarding-voice-df_eva")).toBeVisible({ timeout: 30_000 });
|
|
await expect(page.getByTestId("onboarding-voice-dm_thorsten")).toBeVisible();
|
|
await expect(page.getByTestId("onboarding-voice-af_alloy")).toHaveCount(0);
|
|
await expect(page.getByTestId("onboarding-voice-dm_thorsten")).toHaveAttribute(
|
|
"aria-pressed",
|
|
"true",
|
|
);
|
|
});
|
|
|
|
test("main conversation does not show a group subtitle for one Oo", async ({ page }) => {
|
|
await page.addInitScript(() => {
|
|
Object.defineProperty(navigator, "language", { value: "de-DE", configurable: true });
|
|
localStorage.setItem("rakazo.uiLocale", "de");
|
|
});
|
|
const stamp = Date.now();
|
|
await signup(page, `main-1-1-${stamp}@rakazo.test`, "password12", "Solo");
|
|
await completeOnboarding(page, undefined, { mainConversation: true });
|
|
await expect(page.getByTestId("bot-settings-trigger")).not.toContainText("Gruppenunterhaltung");
|
|
await expect(page.getByTestId("oo-group-stage")).toContainText(/1:1/i);
|
|
});
|
|
|
|
test.describe("mobile shell", () => {
|
|
test.use({
|
|
hasTouch: true,
|
|
isMobile: true,
|
|
viewport: { width: 390, height: 844 },
|
|
});
|
|
|
|
test("Escape closes the mobile navigation drawer", async ({ page }) => {
|
|
const stamp = Date.now();
|
|
await signup(page, `mobile-esc-${stamp}@rakazo.test`, "password12", "Escape");
|
|
await completeOnboarding(page);
|
|
await page.getByRole("button", { name: "Open navigation" }).click();
|
|
await expect(page.getByRole("button", { name: "Close navigation" })).toBeVisible();
|
|
await page.keyboard.press("Escape");
|
|
await expect(page.getByRole("button", { name: "Close navigation" })).toHaveCount(0);
|
|
await expect(page.getByTestId("open-navigation")).toBeFocused();
|
|
});
|
|
});
|