forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvoice.spec.ts
More file actions
99 lines (90 loc) · 4.01 KB
/
Copy pathvoice.spec.ts
File metadata and controls
99 lines (90 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import { test, expect } from "@playwright/test";
// E2E for the voice demo — sample-audio path only.
//
// The "Play sample" button is a deterministic test/demo affordance: it
// synchronously injects the canned phrase ("What is the weather in Tokyo?")
// into the chat composer without touching the runtime's `/transcribe`
// endpoint. That keeps this suite stable across environments where Whisper
// or aimock might be unavailable.
//
// The microphone path is intentionally out of scope: MediaRecorder is hard
// to exercise headlessly without mocking, and the mic is the only path that
// actually exercises real transcription. It's covered by the manual QA
// checklist at qa/voice.md.
//
// Stability expectation: 3 consecutive runs against Railway must pass.
test.describe("Voice Input", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/demos/voice");
});
test("page loads with sample button, chat composer, and mic affordance", async ({
page,
}) => {
await expect(
page.getByRole("heading", { name: "Voice input" }),
).toBeVisible();
await expect(
page.locator('[data-testid="voice-sample-audio-button"]'),
).toBeEnabled();
await expect(page.getByText("Try a sample audio")).toBeVisible();
await expect(
page.locator('[data-testid="copilot-chat-input"]'),
).toBeVisible();
// The mic button is the authoritative signal that the runtime advertised
// `audioFileTranscriptionEnabled: true` — i.e. transcriptionService is
// wired on /api/copilotkit-voice. Exposed by react-core's v2 CopilotChatInput.
// It renders after the /info round trip resolves on the client, which on
// a cold dev server can exceed Playwright's 5s default — give it room.
await expect(
page.locator('[data-testid="copilot-start-transcribe-button"]'),
).toBeVisible({ timeout: 15_000 });
});
test("sample audio button injects the canned phrase into the input", async ({
page,
}) => {
const sampleButton = page.locator(
'[data-testid="voice-sample-audio-button"]',
);
const textarea = page.locator('[data-testid="copilot-chat-textarea"]');
await expect(sampleButton).toBeEnabled();
await expect(textarea).toHaveValue("");
await sampleButton.click();
// The button is synchronous — clicking immediately populates the
// textarea with the canned sample text. No transient "Transcribing…"
// state, no /transcribe round trip.
await expect(textarea).toHaveValue(/weather|tokyo/i, { timeout: 1000 });
await expect(sampleButton).toBeEnabled();
});
test("sending the transcribed text produces a weather tool render", async ({
page,
}) => {
// The end-to-end flow (click → run agent → first assistant chunk) can run
// up to ~50s on a cold langgraph dev server, so override the default 30s
// suite timeout to give the locator's own 45s timeout headroom.
test.setTimeout(90_000);
const sampleButton = page.locator(
'[data-testid="voice-sample-audio-button"]',
);
const textarea = page.locator('[data-testid="copilot-chat-textarea"]');
const sendButton = page.locator('[data-testid="copilot-send-button"]');
await sampleButton.click();
await expect(textarea).toHaveValue(/weather|tokyo/i, { timeout: 1000 });
await sendButton.click();
// The voice-demo route reuses the neutral sample_agent graph, which
// doesn't itself render a weather card — but if the runtime has a
// tool-rendering configuration that handles weather, one of these will
// be visible. The assertion is permissive: we care that *some*
// agent-authored response surface appeared, not exactly which renderer
// was used.
const assistantOrTool = page
.locator(
[
'[data-testid="weather-card"]',
'[data-testid="custom-catchall-card"][data-tool-name="get_weather"]',
'[data-testid="copilot-assistant-message"]',
].join(", "),
)
.first();
await expect(assistantOrTool).toBeVisible({ timeout: 45000 });
});
});