86 lines
2.9 KiB
TypeScript
86 lines
2.9 KiB
TypeScript
import { expect, test } from "playwright/test";
|
|
|
|
test("speech capability detection preserves the server hydration tree", async ({ page }) => {
|
|
const hydrationErrors: string[] = [];
|
|
|
|
page.on("console", (message) => {
|
|
if (message.type() === "error" && /hydration|server rendered html/i.test(message.text())) {
|
|
hydrationErrors.push(message.text());
|
|
}
|
|
});
|
|
|
|
await page.addInitScript(() => {
|
|
Object.defineProperty(window, "SpeechRecognition", {
|
|
configurable: true,
|
|
value: class SpeechRecognition {}
|
|
});
|
|
});
|
|
await page.goto("/", { waitUntil: "networkidle" });
|
|
|
|
await expect(page.getByRole("button", { name: "语音输入" })).toBeVisible();
|
|
await expect(page.getByRole("button", { name: "权限批准模式" })).toBeVisible();
|
|
expect(hydrationErrors).toEqual([]);
|
|
});
|
|
|
|
test("speech recognition failure clears the active animation and explains the error", async ({ page }) => {
|
|
await page.addInitScript(() => {
|
|
Object.defineProperty(window, "SpeechRecognition", {
|
|
configurable: true,
|
|
value: class SpeechRecognition {
|
|
onerror: ((event: { error: string }) => void) | null = null;
|
|
onend: (() => void) | null = null;
|
|
|
|
start() {
|
|
window.setTimeout(() => {
|
|
this.onerror?.({ error: "not-allowed" });
|
|
this.onend?.();
|
|
}, 20);
|
|
}
|
|
|
|
stop() {
|
|
this.onend?.();
|
|
}
|
|
|
|
abort() {}
|
|
}
|
|
});
|
|
});
|
|
await page.goto("/", { waitUntil: "networkidle" });
|
|
|
|
await page.getByRole("button", { name: "语音输入" }).click();
|
|
|
|
await expect(page.getByRole("button", { name: "语音输入" })).toHaveAttribute("aria-pressed", "false");
|
|
await expect(page.locator('[data-slot="voice-input-pulse"]')).toHaveCount(0);
|
|
await expect(page.getByText("无法使用麦克风,请在浏览器地址栏允许麦克风权限后重试")).toBeVisible();
|
|
});
|
|
|
|
test("speech recognition writes final transcript into the prompt", async ({ page }) => {
|
|
await page.addInitScript(() => {
|
|
Object.defineProperty(window, "SpeechRecognition", {
|
|
configurable: true,
|
|
value: class SpeechRecognition {
|
|
onresult: ((event: { resultIndex: number; results: Array<Array<{ transcript: string }> & { isFinal: boolean }> }) => void) | null = null;
|
|
onend: (() => void) | null = null;
|
|
|
|
start() {
|
|
window.setTimeout(() => {
|
|
const result = Object.assign([{ transcript: "检查城南泵站水位" }], { isFinal: true });
|
|
this.onresult?.({ resultIndex: 0, results: [result] });
|
|
}, 20);
|
|
}
|
|
|
|
stop() {
|
|
this.onend?.();
|
|
}
|
|
|
|
abort() {}
|
|
}
|
|
});
|
|
});
|
|
await page.goto("/", { waitUntil: "networkidle" });
|
|
|
|
await page.getByRole("button", { name: "语音输入" }).click();
|
|
|
|
await expect(page.getByPlaceholder("输入调度问题,Agent 将通过后端会话流式响应")).toHaveValue("检查城南泵站水位");
|
|
});
|