feat: Improve UI layout and navigation
- Increase logo size (48x48 desktop, 56x56 mobile) for better visibility - Add logo as favicon - Add logo to mobile header - Move user menu to navigation bars (sidebar on desktop, bottom bar on mobile) - Fix desktop chat layout - container structure prevents voice controls cutoff - Fix mobile bottom bar - use icon-only ActionIcons instead of truncated text buttons - Hide Create Node/New Conversation buttons on mobile to save header space - Make fixed header and voice controls work properly with containers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
143
tests/voice-mode.spec.ts
Normal file
143
tests/voice-mode.spec.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Voice Mode', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Navigate to chat page (should be authenticated via setup)
|
||||
await page.goto('/chat');
|
||||
await expect(page.getByText('Ponderants Interview')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should start voice conversation and display correct button text', async ({ page }) => {
|
||||
// Initial state - button should show "Start Voice Conversation"
|
||||
const voiceButton = page.getByRole('button', { name: /Start Voice Conversation/i });
|
||||
await expect(voiceButton).toBeVisible();
|
||||
|
||||
// Click to start voice mode
|
||||
await voiceButton.click();
|
||||
|
||||
// Button should transition to one of the active states
|
||||
// Could be "Generating speech..." if there's a greeting, or "Listening..." if no greeting
|
||||
await expect(page.getByRole('button', { name: /Generating speech|Listening|Checking for greeting/i })).toBeVisible({
|
||||
timeout: 5000,
|
||||
});
|
||||
});
|
||||
|
||||
test('should skip audio during generation and transition to listening', async ({ page }) => {
|
||||
// Start voice mode
|
||||
const voiceButton = page.getByRole('button', { name: /Start Voice Conversation/i });
|
||||
await voiceButton.click();
|
||||
|
||||
// Wait for generation or playing state
|
||||
await expect(page.getByRole('button', { name: /Generating speech|AI is speaking/i })).toBeVisible({
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
// Skip button should be visible
|
||||
const skipButton = page.getByRole('button', { name: /Skip/i });
|
||||
await expect(skipButton).toBeVisible();
|
||||
|
||||
// Click skip
|
||||
await skipButton.click();
|
||||
|
||||
// Should transition to listening state
|
||||
await expect(page.getByRole('button', { name: /Listening/i })).toBeVisible({ timeout: 3000 });
|
||||
});
|
||||
|
||||
test('should use test buttons to simulate full conversation flow', async ({ page }) => {
|
||||
// Start voice mode
|
||||
await page.getByRole('button', { name: /Start Voice Conversation/i }).click();
|
||||
|
||||
// Wait for initial state (could be checking, generating, or listening)
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// If there's a skip button (greeting is playing), click it
|
||||
const skipButton = page.getByRole('button', { name: /Skip/i });
|
||||
if (await skipButton.isVisible()) {
|
||||
await skipButton.click();
|
||||
}
|
||||
|
||||
// Should eventually reach listening state
|
||||
await expect(page.getByRole('button', { name: /Listening/i })).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// In development mode, test buttons should be visible
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
if (isDevelopment) {
|
||||
// Click "Simulate User Speech" test button
|
||||
const simulateSpeechButton = page.getByRole('button', { name: /Simulate Speech/i });
|
||||
await expect(simulateSpeechButton).toBeVisible();
|
||||
await simulateSpeechButton.click();
|
||||
|
||||
// Should transition to userSpeaking state
|
||||
await expect(page.getByRole('button', { name: /Speaking/i })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Add a phrase using test button
|
||||
const addPhraseButton = page.getByRole('button', { name: /Add Phrase/i });
|
||||
await addPhraseButton.click();
|
||||
|
||||
// Should be in timingOut state
|
||||
await expect(page.getByRole('button', { name: /auto-submit/i })).toBeVisible({ timeout: 2000 });
|
||||
|
||||
// Trigger timeout using test button
|
||||
const triggerTimeoutButton = page.getByRole('button', { name: /Trigger Timeout/i });
|
||||
await triggerTimeoutButton.click();
|
||||
|
||||
// Should submit and wait for AI
|
||||
await expect(page.getByRole('button', { name: /Submitting|Waiting for AI/i })).toBeVisible({
|
||||
timeout: 2000,
|
||||
});
|
||||
|
||||
// Wait for AI response (this will take a few seconds)
|
||||
await expect(page.getByRole('button', { name: /Generating speech|AI is speaking/i })).toBeVisible({
|
||||
timeout: 15000,
|
||||
});
|
||||
|
||||
// Skip the AI audio
|
||||
const skipAudioButton = page.getByRole('button', { name: /Skip/i });
|
||||
if (await skipAudioButton.isVisible()) {
|
||||
await skipAudioButton.click();
|
||||
}
|
||||
|
||||
// Should return to listening
|
||||
await expect(page.getByRole('button', { name: /Listening/i })).toBeVisible({ timeout: 3000 });
|
||||
}
|
||||
});
|
||||
|
||||
test('should stop voice mode and return to idle', async ({ page }) => {
|
||||
// Start voice mode
|
||||
const voiceButton = page.getByRole('button', { name: /Start Voice Conversation/i });
|
||||
await voiceButton.click();
|
||||
|
||||
// Wait for active state
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Click the button again to stop
|
||||
await page.getByRole('button', { name: /Listening|Speaking|Generating|AI is speaking/i }).click();
|
||||
|
||||
// Should return to idle state
|
||||
await expect(page.getByRole('button', { name: /Start Voice Conversation/i })).toBeVisible({
|
||||
timeout: 2000,
|
||||
});
|
||||
});
|
||||
|
||||
test('should disable text input while voice mode is active', async ({ page }) => {
|
||||
const textInput = page.getByPlaceholder(/type your thoughts here/i);
|
||||
|
||||
// Text input should be enabled initially
|
||||
await expect(textInput).toBeEnabled();
|
||||
|
||||
// Start voice mode
|
||||
await page.getByRole('button', { name: /Start Voice Conversation/i }).click();
|
||||
|
||||
// Wait for voice mode to activate
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Text input should be disabled
|
||||
await expect(textInput).toBeDisabled();
|
||||
|
||||
// Stop voice mode
|
||||
await page.getByRole('button', { name: /Listening|Speaking|Generating|AI is speaking/i }).click();
|
||||
|
||||
// Text input should be enabled again
|
||||
await expect(textInput).toBeEnabled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user