use std::time::Instant;
use crate::cli::TestScope;
use crate::doctor::{self, Tool};
use crate::error::CliError;
use crate::project::{App, ensure_page_toolchain, observe_step};
use super::build::build_page_if_stale;
struct Outcome {
scope: &'static str,
passed: bool,
seconds: f64,
}
pub fn test(scope: TestScope) -> Result<(), CliError> {
let app = App::find_from_current_dir()?;
doctor::require(&prerequisites_for(scope), "frame test")?;
if scope.browser_selected() {
doctor::require_chrome().map_err(|reason| CliError::MissingChrome { reason })?;
}
if scope.host_selected() || scope.browser_selected() {
ensure_page_toolchain(&app)?;
}
let mut outcomes: Vec<Outcome> = Vec::new();
if scope.component_selected() {
let start = Instant::now();
let passed = observe_step("gleam", &["test"], &app.component_dir())?;
outcomes.push(outcome("component", passed, start));
}
if scope.host_selected() {
let start = Instant::now();
let page_current = match build_page_if_stale(&app) {
Ok(()) => true,
Err(CliError::CommandFailed { command, status }) => {
eprintln!(
"page compile failed before the host tests: `{command}` exited with {status}"
);
false
}
Err(other) => return Err(other),
};
let passed = page_current && observe_step("cargo", &["test", "--workspace"], &app.root)?;
outcomes.push(outcome("host", passed, start));
}
if scope.browser_selected() {
let start = Instant::now();
let passed = observe_step("npm", &["run", "e2e"], &app.page_dir())?;
outcomes.push(outcome("browser", passed, start));
}
verdict("frame test", &outcomes)
}
fn outcome(scope: &'static str, passed: bool, start: Instant) -> Outcome {
Outcome {
scope,
passed,
seconds: start.elapsed().as_secs_f64(),
}
}
fn verdict(verb: &'static str, outcomes: &[Outcome]) -> Result<(), CliError> {
println!();
println!("{verb} verdict:");
for entry in outcomes {
let mark = if entry.passed { "PASS" } else { "FAIL" };
println!(" {:<10} {mark} {:.1}s", entry.scope, entry.seconds);
}
let failed: Vec<String> = outcomes
.iter()
.filter(|entry| !entry.passed)
.map(|entry| entry.scope.to_owned())
.collect();
if failed.is_empty() {
println!("{verb}: PASS");
Ok(())
} else {
Err(CliError::VerdictFailed { verb, failed })
}
}
fn prerequisites_for(scope: TestScope) -> Vec<&'static Tool> {
let mut tools: Vec<&'static Tool> = vec![&doctor::GLEAM, &doctor::ERLANG];
if scope.host_selected() || scope.browser_selected() {
tools.push(&doctor::RUST);
tools.push(&doctor::NODE);
tools.push(&doctor::NPM);
}
tools
}