auto-claude: subtask-7-3 - Create chatbot E2E tests for message flow and error handling
Add comprehensive E2E tests for the chatbot component including: - Chatbot UI tests (open/close, welcome message, suggested prompts) - Message flow tests (sending messages, receiving responses, enter key submission) - Error handling tests (API failures, streaming errors, dismissible errors) - Clear chat functionality tests - Accessibility tests (ARIA labels, focus management) - Mobile responsiveness tests - Chat history loading tests Tests use mocked API responses for reliability and isolation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,709 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Chatbot E2E Test Suite
|
||||
*
|
||||
* This suite tests the AI chatbot functionality including:
|
||||
* - Opening and closing the chat window
|
||||
* - Message flow (sending and receiving)
|
||||
* - Error handling and validation
|
||||
* - UI interactions (clear chat, suggested prompts)
|
||||
* - Accessibility features
|
||||
*
|
||||
* Note: These tests use mocked API responses for reliability.
|
||||
* Integration tests with the real API should be run separately.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Chatbot UI Tests
|
||||
*
|
||||
* Verify that the chatbot UI renders correctly and interactions work.
|
||||
*/
|
||||
test.describe('Chatbot UI', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/de', {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
});
|
||||
|
||||
test('Chatbot floating button is visible', async ({ page }) => {
|
||||
// Verify the floating chat button exists
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await expect(chatButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('Chatbot opens when floating button is clicked', async ({ page }) => {
|
||||
// Click the chat button
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Verify chat window opens
|
||||
const chatWindow = page.locator('.fixed.z-50.bg-zinc-900');
|
||||
await expect(chatWindow).toBeVisible();
|
||||
|
||||
// Verify chat header is visible
|
||||
const chatHeader = page.locator('text=AI Assistant');
|
||||
await expect(chatHeader).toBeVisible();
|
||||
});
|
||||
|
||||
test('Chatbot closes when close button is clicked', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Wait for chat window to be visible
|
||||
const chatWindow = page.locator('.fixed.z-50.bg-zinc-900');
|
||||
await expect(chatWindow).toBeVisible();
|
||||
|
||||
// Click close button
|
||||
const closeButton = page.locator('button[aria-label="Close chat"]');
|
||||
await closeButton.click();
|
||||
|
||||
// Wait for animation and verify chat is closed
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// Floating button should be visible again
|
||||
await expect(chatButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('Chatbot shows welcome message when empty', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Verify welcome message
|
||||
const welcomeHeading = page.locator("text=Hi, I'm Damjan's AI Assistant");
|
||||
await expect(welcomeHeading).toBeVisible();
|
||||
|
||||
// Verify help text
|
||||
const helpText = page.locator('text=I can help you learn about');
|
||||
await expect(helpText).toBeVisible();
|
||||
});
|
||||
|
||||
test('Chatbot shows suggested prompts when empty', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Verify "Try asking:" label
|
||||
const tryAskingLabel = page.locator('text=Try asking:');
|
||||
await expect(tryAskingLabel).toBeVisible();
|
||||
|
||||
// Verify suggested prompts are visible
|
||||
const suggestedPrompts = page.locator('button:has-text("What services do you offer?")');
|
||||
await expect(suggestedPrompts).toBeVisible();
|
||||
|
||||
const experiencePrompt = page.locator('button:has-text("Tell me about your experience")');
|
||||
await expect(experiencePrompt).toBeVisible();
|
||||
|
||||
const techPrompt = page.locator('button:has-text("What technologies do you work with?")');
|
||||
await expect(techPrompt).toBeVisible();
|
||||
});
|
||||
|
||||
test('Chat input field is present and functional', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Verify input field
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await expect(inputField).toBeVisible();
|
||||
await expect(inputField).toBeEnabled();
|
||||
|
||||
// Verify placeholder
|
||||
await expect(inputField).toHaveAttribute('placeholder', 'Type a message...');
|
||||
|
||||
// Type in input
|
||||
await inputField.fill('Test message');
|
||||
await expect(inputField).toHaveValue('Test message');
|
||||
});
|
||||
|
||||
test('Send button is disabled when input is empty', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Verify send button is disabled with empty input
|
||||
const sendButton = page.locator('button[aria-label="Send message"]');
|
||||
await expect(sendButton).toBeDisabled();
|
||||
|
||||
// Type in input
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await inputField.fill('Test message');
|
||||
|
||||
// Verify send button is now enabled
|
||||
await expect(sendButton).toBeEnabled();
|
||||
});
|
||||
|
||||
test('Privacy note is displayed', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Verify privacy note
|
||||
const privacyNote = page.locator('text=Messages may be stored to improve service');
|
||||
await expect(privacyNote).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Chatbot Message Flow Tests
|
||||
*
|
||||
* Verify that sending and receiving messages works correctly.
|
||||
* These tests mock API responses for reliability.
|
||||
*/
|
||||
test.describe('Chatbot Message Flow', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Mock the chat API endpoint
|
||||
await page.route('**/api/chat', async (route) => {
|
||||
const request = route.request();
|
||||
const method = request.method();
|
||||
|
||||
if (method === 'POST') {
|
||||
// Simulate streaming response for POST
|
||||
const encoder = new TextEncoder();
|
||||
const chunks = [
|
||||
'data: {"content":"Hello","sessionId":"test-session"}\n\n',
|
||||
'data: {"content":"! How can I","sessionId":"test-session"}\n\n',
|
||||
'data: {"content":" help you today?","sessionId":"test-session"}\n\n',
|
||||
'data: {"done":true,"sessionId":"test-session"}\n\n',
|
||||
];
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
},
|
||||
body: chunks.join(''),
|
||||
});
|
||||
} else if (method === 'GET') {
|
||||
// Return empty history for GET
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ messages: [], sessionId: 'test-session' }),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/de', {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
});
|
||||
|
||||
test('Sending a message shows user message in chat', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Type and send message
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await inputField.fill('Hello, how are you?');
|
||||
|
||||
const sendButton = page.locator('button[aria-label="Send message"]');
|
||||
await sendButton.click();
|
||||
|
||||
// Verify user message appears in chat
|
||||
const userMessage = page.locator('text=Hello, how are you?');
|
||||
await expect(userMessage).toBeVisible();
|
||||
|
||||
// Input should be cleared after sending
|
||||
await expect(inputField).toHaveValue('');
|
||||
});
|
||||
|
||||
test('Sending a message triggers API response', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Type and send message
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await inputField.fill('Hello');
|
||||
|
||||
const sendButton = page.locator('button[aria-label="Send message"]');
|
||||
await sendButton.click();
|
||||
|
||||
// Wait for assistant response
|
||||
const assistantMessage = page.locator('text=Hello! How can I help you today?');
|
||||
await expect(assistantMessage).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('Clicking suggested prompt sends message', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Click suggested prompt
|
||||
const suggestedPrompt = page.locator('button:has-text("What services do you offer?")');
|
||||
await suggestedPrompt.click();
|
||||
|
||||
// Verify user message appears
|
||||
const userMessage = page.locator('text=What services do you offer?');
|
||||
await expect(userMessage).toBeVisible();
|
||||
});
|
||||
|
||||
test('Enter key submits the message', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Type message and press Enter
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await inputField.fill('Test enter submission');
|
||||
await inputField.press('Enter');
|
||||
|
||||
// Verify user message appears
|
||||
const userMessage = page.locator('text=Test enter submission');
|
||||
await expect(userMessage).toBeVisible();
|
||||
});
|
||||
|
||||
test('Input is disabled during message sending', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Type and send message
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await inputField.fill('Test message');
|
||||
|
||||
const sendButton = page.locator('button[aria-label="Send message"]');
|
||||
await sendButton.click();
|
||||
|
||||
// Input should be disabled during loading
|
||||
await expect(inputField).toBeDisabled();
|
||||
|
||||
// Wait for response to complete
|
||||
await page.waitForTimeout(500);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Chatbot Error Handling Tests
|
||||
*
|
||||
* Verify that errors are handled gracefully.
|
||||
*/
|
||||
test.describe('Chatbot Error Handling', () => {
|
||||
test('Shows error message when API fails', async ({ page }) => {
|
||||
// Mock API to return error
|
||||
await page.route('**/api/chat', async (route) => {
|
||||
const method = route.request().method();
|
||||
|
||||
if (method === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 500,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ error: 'Internal server error' }),
|
||||
});
|
||||
} else if (method === 'GET') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ messages: [], sessionId: 'test-session' }),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/de', {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Send message
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await inputField.fill('Test message');
|
||||
|
||||
const sendButton = page.locator('button[aria-label="Send message"]');
|
||||
await sendButton.click();
|
||||
|
||||
// Wait for error message
|
||||
const errorContainer = page.locator('.bg-red-900\\/20');
|
||||
await expect(errorContainer).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('Error message can be dismissed', async ({ page }) => {
|
||||
// Mock API to return error
|
||||
await page.route('**/api/chat', async (route) => {
|
||||
const method = route.request().method();
|
||||
|
||||
if (method === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 500,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ error: 'Test error' }),
|
||||
});
|
||||
} else if (method === 'GET') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ messages: [], sessionId: 'test-session' }),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/de', {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Send message to trigger error
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await inputField.fill('Test message');
|
||||
|
||||
const sendButton = page.locator('button[aria-label="Send message"]');
|
||||
await sendButton.click();
|
||||
|
||||
// Wait for error and dismiss it
|
||||
const errorContainer = page.locator('.bg-red-900\\/20');
|
||||
await expect(errorContainer).toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Click dismiss button
|
||||
const dismissButton = page.locator('text=Dismiss');
|
||||
await dismissButton.click();
|
||||
|
||||
// Error should be hidden
|
||||
await expect(errorContainer).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('Handles streaming error gracefully', async ({ page }) => {
|
||||
// Mock API to return error in stream
|
||||
await page.route('**/api/chat', async (route) => {
|
||||
const method = route.request().method();
|
||||
|
||||
if (method === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
},
|
||||
body: 'data: {"error":"Stream error occurred"}\n\n',
|
||||
});
|
||||
} else if (method === 'GET') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ messages: [], sessionId: 'test-session' }),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/de', {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Send message
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await inputField.fill('Test message');
|
||||
|
||||
const sendButton = page.locator('button[aria-label="Send message"]');
|
||||
await sendButton.click();
|
||||
|
||||
// Error should be shown
|
||||
const errorContainer = page.locator('.bg-red-900\\/20');
|
||||
await expect(errorContainer).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Chatbot Clear Chat Tests
|
||||
*
|
||||
* Verify that clearing chat works correctly.
|
||||
*/
|
||||
test.describe('Chatbot Clear Chat', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Mock the chat API endpoint
|
||||
await page.route('**/api/chat', async (route) => {
|
||||
const method = route.request().method();
|
||||
|
||||
if (method === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
},
|
||||
body:
|
||||
'data: {"content":"Test response","sessionId":"test-session"}\n\ndata: {"done":true,"sessionId":"test-session"}\n\n',
|
||||
});
|
||||
} else if (method === 'GET') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ messages: [], sessionId: 'test-session' }),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/de', {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
});
|
||||
|
||||
test('Clear chat button appears after sending message', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Clear button should not be visible initially (no messages)
|
||||
const clearButton = page.locator('button[aria-label="Clear chat"]');
|
||||
await expect(clearButton).not.toBeVisible();
|
||||
|
||||
// Send a message
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await inputField.fill('Test message');
|
||||
|
||||
const sendButton = page.locator('button[aria-label="Send message"]');
|
||||
await sendButton.click();
|
||||
|
||||
// Wait for message to appear
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Clear button should now be visible
|
||||
await expect(clearButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('Clicking clear chat removes all messages', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Send a message
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await inputField.fill('Test message');
|
||||
|
||||
const sendButton = page.locator('button[aria-label="Send message"]');
|
||||
await sendButton.click();
|
||||
|
||||
// Wait for messages
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Click clear button
|
||||
const clearButton = page.locator('button[aria-label="Clear chat"]');
|
||||
await clearButton.click();
|
||||
|
||||
// Verify messages are cleared and welcome message returns
|
||||
const welcomeHeading = page.locator("text=Hi, I'm Damjan's AI Assistant");
|
||||
await expect(welcomeHeading).toBeVisible();
|
||||
|
||||
// Clear button should be hidden again
|
||||
await expect(clearButton).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Chatbot Accessibility Tests
|
||||
*
|
||||
* Verify that the chatbot is accessible.
|
||||
*/
|
||||
test.describe('Chatbot Accessibility', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Mock the chat API
|
||||
await page.route('**/api/chat', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ messages: [], sessionId: 'test-session' }),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/de', {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
});
|
||||
|
||||
test('Chatbot floating button has aria-label', async ({ page }) => {
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await expect(chatButton).toBeVisible();
|
||||
await expect(chatButton).toHaveAttribute('aria-label', 'Open chat');
|
||||
});
|
||||
|
||||
test('Close button has aria-label', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
const closeButton = page.locator('button[aria-label="Close chat"]');
|
||||
await expect(closeButton).toBeVisible();
|
||||
await expect(closeButton).toHaveAttribute('aria-label', 'Close chat');
|
||||
});
|
||||
|
||||
test('Input field has aria-label', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await expect(inputField).toBeVisible();
|
||||
await expect(inputField).toHaveAttribute('aria-label', 'Chat message input');
|
||||
});
|
||||
|
||||
test('Send button has aria-label', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
const sendButton = page.locator('button[aria-label="Send message"]');
|
||||
await expect(sendButton).toBeVisible();
|
||||
await expect(sendButton).toHaveAttribute('aria-label', 'Send message');
|
||||
});
|
||||
|
||||
test('Input field receives focus when chat opens', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Wait for focus to be set
|
||||
await page.waitForTimeout(200);
|
||||
|
||||
// Verify input has focus
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await expect(inputField).toBeFocused();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Chatbot Mobile Tests
|
||||
*
|
||||
* Verify that the chatbot works correctly on mobile devices.
|
||||
*/
|
||||
test.describe('Chatbot Mobile', () => {
|
||||
test.use({ viewport: { width: 375, height: 812 } });
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Mock the chat API
|
||||
await page.route('**/api/chat', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ messages: [], sessionId: 'test-session' }),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/de', {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
});
|
||||
|
||||
test('Chatbot floating button is visible on mobile', async ({ page }) => {
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await expect(chatButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('Chatbot opens correctly on mobile', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Verify chat window is visible
|
||||
const chatWindow = page.locator('.fixed.z-50.bg-zinc-900');
|
||||
await expect(chatWindow).toBeVisible();
|
||||
|
||||
// Verify chat is usable
|
||||
const inputField = page.locator('input[aria-label="Chat message input"]');
|
||||
await expect(inputField).toBeVisible();
|
||||
});
|
||||
|
||||
test('Chat window does not overflow on mobile', async ({ page }) => {
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Get chat window dimensions
|
||||
const chatWindow = page.locator('.fixed.z-50.bg-zinc-900');
|
||||
const box = await chatWindow.boundingBox();
|
||||
|
||||
expect(box).not.toBeNull();
|
||||
if (box) {
|
||||
// Chat window should fit within viewport
|
||||
expect(box.x).toBeGreaterThanOrEqual(0);
|
||||
expect(box.y).toBeGreaterThanOrEqual(0);
|
||||
expect(box.x + box.width).toBeLessThanOrEqual(375 + 1); // Allow 1px tolerance
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Chatbot Chat History Tests
|
||||
*
|
||||
* Verify that chat history is loaded correctly.
|
||||
*/
|
||||
test.describe('Chatbot Chat History', () => {
|
||||
test('Loads chat history when opening', async ({ page }) => {
|
||||
// Mock API with existing history
|
||||
await page.route('**/api/chat', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
messages: [
|
||||
{ id: '1', role: 'user', content: 'Previous question', created_at: new Date().toISOString() },
|
||||
{ id: '2', role: 'assistant', content: 'Previous answer', created_at: new Date().toISOString() },
|
||||
],
|
||||
sessionId: 'existing-session',
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/de', {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Verify previous messages are loaded
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const previousQuestion = page.locator('text=Previous question');
|
||||
await expect(previousQuestion).toBeVisible();
|
||||
|
||||
const previousAnswer = page.locator('text=Previous answer');
|
||||
await expect(previousAnswer).toBeVisible();
|
||||
});
|
||||
|
||||
test('Shows welcome message when no history exists', async ({ page }) => {
|
||||
// Mock API with empty history
|
||||
await page.route('**/api/chat', async (route) => {
|
||||
if (route.request().method() === 'GET') {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({
|
||||
messages: [],
|
||||
sessionId: 'new-session',
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/de', {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
|
||||
// Open chat
|
||||
const chatButton = page.locator('button[aria-label="Open chat"]');
|
||||
await chatButton.click();
|
||||
|
||||
// Verify welcome message is shown
|
||||
const welcomeHeading = page.locator("text=Hi, I'm Damjan's AI Assistant");
|
||||
await expect(welcomeHeading).toBeVisible();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user