Copying chat example across
This commit is contained in:
parent
de9e20bf89
commit
f5f1943ac5
2 changed files with 7675 additions and 0 deletions
47
examples/chat-ui/e2e/build.spec.ts
Normal file
47
examples/chat-ui/e2e/build.spec.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
interface ExecError extends Error {
|
||||
stdout?: string
|
||||
stderr?: string
|
||||
}
|
||||
|
||||
test.describe('Build Tests', () => {
|
||||
test('should build without type errors', async () => {
|
||||
try {
|
||||
const { stderr } = await execAsync('pnpm build', {
|
||||
cwd: process.cwd(),
|
||||
timeout: 60000
|
||||
})
|
||||
|
||||
// Check for TypeScript compilation errors
|
||||
expect(stderr).not.toContain('error TS')
|
||||
expect(stderr).not.toContain('Type error')
|
||||
|
||||
} catch (error) {
|
||||
const execError = error as ExecError
|
||||
console.error('Build failed:', execError.stdout, execError.stderr)
|
||||
throw new Error(`Build failed: ${execError.message}`)
|
||||
}
|
||||
})
|
||||
|
||||
test('should lint without errors', async () => {
|
||||
try {
|
||||
const { stderr } = await execAsync('pnpm lint', {
|
||||
cwd: process.cwd(),
|
||||
timeout: 30000
|
||||
})
|
||||
|
||||
// ESLint should pass without errors
|
||||
expect(stderr).not.toContain('error')
|
||||
|
||||
} catch (error) {
|
||||
const execError = error as ExecError
|
||||
console.error('Lint failed:', execError.stdout, execError.stderr)
|
||||
throw new Error(`Lint failed: ${execError.message}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
167
examples/chat-ui/e2e/chat.spec.ts
Normal file
167
examples/chat-ui/e2e/chat.spec.ts
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('Chat Functionality', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Mock the external AI API calls
|
||||
await page.route('https://api.groq.com/**', async route => {
|
||||
// Mock streaming response for Groq
|
||||
const mockResponse = 'Hello! This is a mocked response from the AI assistant.'
|
||||
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
body: mockResponse,
|
||||
})
|
||||
})
|
||||
|
||||
await page.route('https://api.anthropic.com/**', async route => {
|
||||
// Mock streaming response for Anthropic
|
||||
const mockResponse = 'Hello! This is a mocked response from Claude.'
|
||||
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
body: mockResponse,
|
||||
})
|
||||
})
|
||||
|
||||
// Mock the local API endpoint
|
||||
await page.route('/api/chat', async route => {
|
||||
const mockResponse = {
|
||||
choices: [{
|
||||
delta: {
|
||||
content: 'Hello! This is a mocked response from the local API.'
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(mockResponse),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('should load the chat interface', async ({ page }) => {
|
||||
await page.goto('/')
|
||||
|
||||
// Check if the main elements are present
|
||||
await expect(page.locator('body')).toBeVisible()
|
||||
|
||||
// Look for chat-related elements (adjust selectors based on your UI)
|
||||
const messageInput = page.locator('textarea, input[type="text"]').first()
|
||||
await expect(messageInput).toBeVisible()
|
||||
|
||||
// Check for a send button or similar
|
||||
const sendButton = page.locator('button').filter({ hasText: /send|submit/i }).first()
|
||||
if (await sendButton.isVisible()) {
|
||||
await expect(sendButton).toBeVisible()
|
||||
}
|
||||
})
|
||||
|
||||
test('should send a message and receive a response', async ({ page }) => {
|
||||
await page.goto('/')
|
||||
|
||||
// Wait for the page to load
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Find the message input field
|
||||
const messageInput = page.locator('textarea, input[type="text"]').first()
|
||||
await expect(messageInput).toBeVisible()
|
||||
|
||||
// Type a test message
|
||||
const testMessage = 'Hello, can you help me?'
|
||||
await messageInput.fill(testMessage)
|
||||
await expect(messageInput).toHaveValue(testMessage)
|
||||
|
||||
// Send the message by pressing Enter (most common method)
|
||||
await messageInput.press('Enter')
|
||||
|
||||
// Wait a moment for the form submission to process
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
// Check that a conversation has started - look for any text content that might be a message
|
||||
const pageContent = await page.textContent('body')
|
||||
expect(pageContent).toContain(testMessage)
|
||||
|
||||
// The input may or may not clear depending on implementation details
|
||||
// This is less critical than the message appearing
|
||||
})
|
||||
|
||||
test('should handle API errors gracefully', async ({ page }) => {
|
||||
// Override mocks to simulate API errors
|
||||
await page.route('https://api.groq.com/**', async route => {
|
||||
route.fulfill({
|
||||
status: 500,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ error: 'Internal server error' }),
|
||||
})
|
||||
})
|
||||
|
||||
await page.route('https://api.anthropic.com/**', async route => {
|
||||
route.fulfill({
|
||||
status: 401,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ error: 'Unauthorized' }),
|
||||
})
|
||||
})
|
||||
|
||||
await page.goto('/')
|
||||
|
||||
// Try to send a message
|
||||
const messageInput = page.locator('textarea, input[type="text"]').first()
|
||||
await messageInput.fill('Test message')
|
||||
|
||||
const sendButton = page.locator('button').filter({ hasText: /send|submit/i }).first()
|
||||
if (await sendButton.isVisible()) {
|
||||
await sendButton.click()
|
||||
} else {
|
||||
await messageInput.press('Enter')
|
||||
}
|
||||
|
||||
// Should handle the error without crashing
|
||||
await page.waitForTimeout(2000)
|
||||
|
||||
// The app should still be functional (not showing a white screen or error page)
|
||||
await expect(page.locator('body')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should persist conversations', async ({ page }) => {
|
||||
await page.goto('/')
|
||||
|
||||
// Send a message
|
||||
const messageInput = page.locator('textarea, input[type="text"]').first()
|
||||
await messageInput.fill('Test message for persistence')
|
||||
|
||||
const sendButton = page.locator('button').filter({ hasText: /send|submit/i }).first()
|
||||
if (await sendButton.isVisible()) {
|
||||
await sendButton.click()
|
||||
} else {
|
||||
await messageInput.press('Enter')
|
||||
}
|
||||
|
||||
await page.waitForTimeout(2000)
|
||||
|
||||
// Refresh the page
|
||||
await page.reload()
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Check if the conversation persisted (this depends on your IndexedDB implementation)
|
||||
// We'll just verify the page loads correctly after refresh
|
||||
await expect(page.locator('body')).toBeVisible()
|
||||
|
||||
const inputAfterReload = page.locator('textarea, input[type="text"]').first()
|
||||
await expect(inputAfterReload).toBeVisible()
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue