#![cfg(feature = "system-tests")]
use autumn_web::system_test::{BrowserCheck, SystemTest, SystemTestError};
#[test]
fn browser_check_reports_result() {
let result = BrowserCheck::run();
match result {
BrowserCheck::Found { path, version } => {
assert!(!path.as_os_str().is_empty());
assert!(!version.is_empty(), "version string must not be empty");
}
BrowserCheck::NotFound { searched_paths } => {
assert!(
!searched_paths.is_empty(),
"must report at least one path that was searched"
);
}
}
}
#[test]
fn browser_check_displays_actionable_message() {
let check = BrowserCheck::run();
let msg = check.to_string();
assert!(!msg.is_empty());
if matches!(check, BrowserCheck::NotFound { .. }) {
assert!(
msg.contains("apt-get") || msg.contains("brew") || msg.contains("AUTUMN_CHROMIUM"),
"not-found message must include install hint; got: {msg}"
);
}
}
#[test]
fn artifact_dir_is_under_target() {
let dir = autumn_web::system_test::artifact_dir("my_test");
let s = dir.to_string_lossy();
assert!(
s.contains("system-tests"),
"artifact dir must be under target/system-tests; got: {s}"
);
assert!(
s.contains("my_test"),
"artifact dir must include the test name; got: {s}"
);
}
#[test]
fn system_test_builder_has_expected_methods() {
fn _assert_api_shape() {
let _builder = SystemTest::new()
.artifact_dir("/tmp/artifacts")
.browser_timeout(std::time::Duration::from_secs(30))
.hx_settle_timeout(std::time::Duration::from_millis(500));
}
}
#[test]
fn system_test_error_displays() {
let e = SystemTestError::BrowserNotFound {
searched: vec!["/usr/bin/chromium".into()],
};
let msg = e.to_string();
assert!(msg.contains("browser") || msg.contains("Chromium") || msg.contains("chromium"));
}
#[test]
fn assertion_error_includes_selector() {
let e = SystemTestError::AssertionFailed {
message: "expected text 'hello' in DOM".into(),
artifact_path: None,
};
let msg = e.to_string();
assert!(msg.contains("hello"));
}
#[tokio::test]
#[ignore = "requires Chromium — set AUTUMN_CHROMIUM or install chromium-browser"]
async fn system_test_boots_and_visits_page() {
use autumn_web::prelude::*;
#[get("/")]
async fn index() -> &'static str {
"<html><body><h1 id='greeting'>Hello from system test</h1></body></html>"
}
let runner = SystemTest::new()
.routes(routes![index])
.build()
.await
.expect("failed to start system test runner");
let page = runner.page().await.expect("failed to open page");
page.visit("/").await.expect("visit failed");
page.expect_text("Hello from system test")
.await
.expect("text assertion failed");
}
#[tokio::test]
#[ignore = "requires Chromium"]
async fn assertion_failure_writes_artifacts() {
use autumn_web::prelude::*;
use std::path::Path;
#[get("/")]
async fn index() -> &'static str {
"<html><body><p>Only this text</p></body></html>"
}
let runner = SystemTest::new()
.routes(routes![index])
.artifact_dir("/tmp/autumn-system-test-artifacts")
.build()
.await
.expect("start runner");
let page = runner.page().await.expect("open page");
page.visit("/").await.expect("visit");
let result = page.expect_text("NOT IN PAGE").await;
assert!(result.is_err(), "should fail for missing text");
if let Err(SystemTestError::AssertionFailed {
artifact_path: Some(p),
..
}) = result
{
assert!(
Path::new(&p).with_extension("png").exists()
|| Path::new(&p).with_extension("html").exists(),
"artifact file not written at {p}"
);
}
}
#[tokio::test]
#[ignore = "requires Chromium"]
async fn expect_hx_settle_waits_for_htmx() {
use autumn_web::prelude::*;
#[get("/")]
async fn index() -> Markup {
maud::html! {
html {
head {
script src="/static/js/htmx.min.js" {}
}
body {
div id="result" {}
button
hx-get="/swap"
hx-target="#result"
hx-swap="innerHTML" { "Click me" }
}
}
}
}
#[get("/swap")]
async fn swap() -> &'static str {
"<span>Swapped!</span>"
}
let runner = SystemTest::new()
.routes(routes![index, swap])
.build()
.await
.expect("start");
let page = runner.page().await.expect("page");
page.visit("/").await.expect("visit");
page.click("button").await.expect("click");
page.expect_hx_settle().await.expect("settle");
page.expect_text("Swapped!").await.expect("assert swap");
}
#[tokio::test]
#[ignore = "requires Chromium"]
async fn click_triggering_full_page_navigation_does_not_break_polling() {
use autumn_web::prelude::*;
#[get("/")]
async fn form_page() -> Markup {
maud::html! {
html {
body {
form action="/submit" method="post" {
button type="submit" { "Go" }
}
}
}
}
}
#[post("/submit")]
async fn submit() -> Redirect {
Redirect::to("/done")
}
#[get("/done")]
async fn done() -> &'static str {
"Navigated successfully"
}
let runner = SystemTest::new()
.routes(routes![form_page, submit, done])
.build()
.await
.expect("start");
let page = runner.page().await.expect("page");
page.visit("/").await.expect("visit");
page.click("button[type=submit]")
.await
.expect("submit form — triggers a full-page redirect");
page.expect_text("Navigated successfully").await.expect(
"text on the post-redirect page must be visible without the poll \
aborting on a transient destroyed-execution-context error",
);
}
#[tokio::test]
#[ignore = "requires Chromium — set AUTUMN_CHROMIUM or install chromium-browser"]
async fn attach_visits_externally_running_server() {
use autumn_web::prelude::*;
#[get("/")]
async fn index() -> &'static str {
"<html><body><h1>Externally booted</h1></body></html>"
}
let server = SystemTest::new()
.routes(routes![index])
.build()
.await
.expect("boot stand-in server");
let base_url = server.base_url().to_string();
let runner = SystemTest::attach(base_url)
.await
.expect("attach to running server");
let page = runner.page().await.expect("open page");
page.visit("/").await.expect("visit");
page.expect_text("Externally booted")
.await
.expect("text assertion failed");
}
#[tokio::test]
#[ignore = "requires Chromium"]
async fn expect_no_console_errors_fails_on_uncaught_exception() {
use autumn_web::prelude::*;
#[get("/")]
async fn index() -> &'static str {
"<html><body><h1>Page loads fine</h1></body></html>"
}
let runner = SystemTest::new()
.routes(routes![index])
.build()
.await
.expect("start runner");
let page = runner.page().await.expect("open page");
page.visit("/").await.expect("visit");
let _ = page
.evaluate("setTimeout(() => { throw new Error('boom'); }, 0)")
.await;
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
let result = page.expect_no_console_errors().await;
assert!(
result.is_err(),
"a page that throws an uncaught exception must fail the console-error assertion"
);
}
#[tokio::test]
#[ignore = "requires Chromium"]
async fn expect_no_console_errors_passes_on_clean_page() {
use autumn_web::prelude::*;
#[get("/")]
async fn index() -> &'static str {
"<html><body><h1>All good</h1></body></html>"
}
let runner = SystemTest::new()
.routes(routes![index])
.build()
.await
.expect("start runner");
let page = runner.page().await.expect("open page");
page.visit("/").await.expect("visit");
page.expect_text("All good").await.expect("text");
page.expect_no_console_errors()
.await
.expect("clean page must not report console errors");
}
#[tokio::test]
#[ignore = "requires Chromium"]
async fn console_errors_returns_accumulated_messages() {
use autumn_web::prelude::*;
#[get("/")]
async fn index() -> &'static str {
"<html><body><h1>Page loads fine</h1></body></html>"
}
let runner = SystemTest::new()
.routes(routes![index])
.build()
.await
.expect("start runner");
let page = runner.page().await.expect("open page");
page.visit("/").await.expect("visit");
let _ = page.evaluate("console.error('bad thing happened')").await;
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
let errors = page.console_errors();
assert!(
errors.iter().any(|e| e.contains("bad thing happened")),
"expected captured console.error message; got: {errors:?}"
);
}