- Add BotServerInstance::start_with_main_stack() for using real LLM - Update E2E tests to auto-start BotServer and BotUI if not running - Prefer Brave browser over Chrome/Chromium for CDP testing - Upgrade chromiumoxide to 0.8 - Add browser window position/size for visibility - Fix chat tests to require BotUI for chat interface - Add browser_service.rs for CDP-based browser management - Remove chromedriver dependency (use CDP directly)
173 lines
5.5 KiB
Rust
173 lines
5.5 KiB
Rust
use super::{should_run_e2e_tests, E2ETestContext};
|
|
use bottest::prelude::*;
|
|
use bottest::web::Locator;
|
|
|
|
/// Simple "hi" chat test with real botserver
|
|
#[tokio::test]
|
|
async fn test_chat_hi() {
|
|
if !should_run_e2e_tests() {
|
|
eprintln!("Skipping: E2E tests disabled");
|
|
return;
|
|
}
|
|
|
|
let ctx = match E2ETestContext::setup_with_browser().await {
|
|
Ok(ctx) => ctx,
|
|
Err(e) => {
|
|
eprintln!("Test failed: {}", e);
|
|
panic!("Failed to setup E2E context: {}", e);
|
|
}
|
|
};
|
|
|
|
if !ctx.has_browser() {
|
|
ctx.close().await;
|
|
panic!("Browser not available - cannot run E2E test");
|
|
}
|
|
|
|
// Chat UI requires botui
|
|
if ctx.ui.is_none() {
|
|
ctx.close().await;
|
|
panic!("BotUI not available - chat tests require botui running on port 3000");
|
|
}
|
|
|
|
let browser = ctx.browser.as_ref().unwrap();
|
|
// Use botui URL for chat (botserver is API only)
|
|
let ui_url = ctx.ui.as_ref().unwrap().url.clone();
|
|
let chat_url = format!("{}/#chat", ui_url);
|
|
|
|
println!("🌐 Navigating to: {}", chat_url);
|
|
|
|
if let Err(e) = browser.goto(&chat_url).await {
|
|
ctx.close().await;
|
|
panic!("Failed to navigate to chat: {}", e);
|
|
}
|
|
|
|
// Wait for page to load and HTMX to initialize chat content
|
|
println!("⏳ Waiting for page to load...");
|
|
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
|
|
|
|
// Chat input: botui uses #messageInput or #ai-input
|
|
let input = Locator::css("#messageInput, #ai-input, .ai-input");
|
|
|
|
// Try to find input with retries (HTMX loads content dynamically)
|
|
let mut found_input = false;
|
|
for attempt in 1..=10 {
|
|
if browser.exists(input.clone()).await {
|
|
found_input = true;
|
|
println!("✓ Chat input found (attempt {})", attempt);
|
|
break;
|
|
}
|
|
println!(" ... waiting for chat input (attempt {}/10)", attempt);
|
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
|
}
|
|
|
|
if !found_input {
|
|
// Take screenshot on failure
|
|
if let Ok(screenshot) = browser.screenshot().await {
|
|
let _ = std::fs::write("/tmp/bottest-chat-fail.png", &screenshot);
|
|
println!("Screenshot saved to /tmp/bottest-chat-fail.png");
|
|
}
|
|
// Also print page source for debugging
|
|
if let Ok(source) = browser.page_source().await {
|
|
let preview: String = source.chars().take(2000).collect();
|
|
println!("Page source preview:\n{}", preview);
|
|
}
|
|
ctx.close().await;
|
|
panic!("Chat input not found after 10 attempts");
|
|
}
|
|
|
|
// Type "hi"
|
|
println!("⌨️ Typing 'hi'...");
|
|
if let Err(e) = browser.type_text(input.clone(), "hi").await {
|
|
ctx.close().await;
|
|
panic!("Failed to type: {}", e);
|
|
}
|
|
|
|
// Click send button or press Enter
|
|
let send_btn = Locator::css("#sendBtn, #ai-send, .ai-send, button[type='submit']");
|
|
match browser.click(send_btn).await {
|
|
Ok(_) => println!("✓ Message sent (click)"),
|
|
Err(_) => {
|
|
// Try Enter key instead
|
|
match browser.press_key(input, "Enter").await {
|
|
Ok(_) => println!("✓ Message sent (Enter key)"),
|
|
Err(e) => println!("⚠ Send may have failed: {}", e),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wait for response
|
|
println!("⏳ Waiting for bot response...");
|
|
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
|
|
|
// Check for response - botui uses .message.bot or .assistant class
|
|
let response =
|
|
Locator::css(".message.bot, .message.assistant, .bot-message, .assistant-message");
|
|
match browser.find_elements(response).await {
|
|
Ok(elements) if !elements.is_empty() => {
|
|
println!("✓ Bot responded! ({} messages)", elements.len());
|
|
}
|
|
_ => {
|
|
println!("⚠ No bot response detected (may need LLM configuration)");
|
|
}
|
|
}
|
|
|
|
// Take final screenshot
|
|
if let Ok(screenshot) = browser.screenshot().await {
|
|
let _ = std::fs::write("/tmp/bottest-chat-result.png", &screenshot);
|
|
println!("📸 Screenshot: /tmp/bottest-chat-result.png");
|
|
}
|
|
|
|
ctx.close().await;
|
|
println!("✅ Chat test complete!");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_chat_page_loads() {
|
|
if !should_run_e2e_tests() {
|
|
return;
|
|
}
|
|
|
|
let ctx = match E2ETestContext::setup_with_browser().await {
|
|
Ok(ctx) => ctx,
|
|
Err(e) => {
|
|
panic!("Setup failed: {}", e);
|
|
}
|
|
};
|
|
|
|
if !ctx.has_browser() {
|
|
ctx.close().await;
|
|
panic!("Browser not available");
|
|
}
|
|
|
|
// Chat UI requires botui
|
|
if ctx.ui.is_none() {
|
|
ctx.close().await;
|
|
panic!("BotUI not available - chat tests require botui. Start it with: cd ../botui && cargo run");
|
|
}
|
|
|
|
let browser = ctx.browser.as_ref().unwrap();
|
|
// Use botui URL for chat (botserver is API only)
|
|
let ui_url = ctx.ui.as_ref().unwrap().url.clone();
|
|
let chat_url = format!("{}/#chat", ui_url);
|
|
|
|
if let Err(e) = browser.goto(&chat_url).await {
|
|
ctx.close().await;
|
|
panic!("Navigation failed: {}", e);
|
|
}
|
|
|
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
|
|
|
let input = Locator::css("#messageInput, input[type='text'], textarea");
|
|
match browser.wait_for(input).await {
|
|
Ok(_) => println!("✓ Chat loaded"),
|
|
Err(e) => {
|
|
if let Ok(s) = browser.screenshot().await {
|
|
let _ = std::fs::write("/tmp/bottest-fail.png", &s);
|
|
}
|
|
ctx.close().await;
|
|
panic!("Chat not loaded: {}", e);
|
|
}
|
|
}
|
|
|
|
ctx.close().await;
|
|
}
|