//! The weekly review, driven through the router against a real database. //! //! Same standard as the screens before it: no Tauri runtime and no window, //! the description asserted, and the markup only where the markup is the point. //! Every workaround this port had to take is asserted here rather than left to //! be noticed, so closing a finding is a test that has to change. use std::sync::Arc; use goingson_core::{NewTask, Priority, weekly_review}; use quasi_http::Serves as _; use quasi_router::Outcome; use quasi_router::{Params, Request, Response}; use super::super::router; use crate::state::{AppState, DESKTOP_USER_ID}; /// State with the desktop user in place, which is who the handlers read as. async fn state() -> Arc { let (state, _) = crate::test_utils::setup_test_state().await; let now = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(); state .db .conn() .unwrap() .execute( "INSERT OR IGNORE INTO users (id, email, password_hash, display_name, created_at) \ VALUES (?, ?, ?, ?, ?)", rusqlite::params![ DESKTOP_USER_ID.to_string(), "desktop@localhost", "x", "Desktop User", &now, ], ) .unwrap(); state } fn add(state: &AppState, title: &str) -> goingson_core::Task { state .tasks .create( DESKTOP_USER_ID, NewTask::builder(title) .title(title) .priority(Priority::High) .build(), ) .unwrap() } /// A task in a project, due on a day that has already gone. fn overdue_in_project(state: &AppState, title: &str, project: &str) { let project = state .projects .create( DESKTOP_USER_ID, goingson_core::NewProject { name: project.to_owned(), description: String::new(), project_type: goingson_core::ProjectType::SideProject, status: goingson_core::ProjectStatus::Active, }, ) .unwrap(); state .tasks .create( DESKTOP_USER_ID, NewTask::builder(title) .priority(Priority::High) .project_id(project.id) .due(chrono::Utc::now() - chrono::Duration::days(3)) .build(), ) .unwrap(); } /// The week the tests write into, which is the one anything created now lands /// in. fn this_week() -> String { weekly_review::current_week_start().to_string() } fn get(state: &AppState, path: &str, params: Params) -> Response { router() .handle(state, Request::get(path).carrying(params)) .expect("the route answers") } fn post(state: &AppState, path: &str, params: Params) -> Response { router() .handle(state, Request::post(path).sending(params)) .expect("the route answers") } fn html(response: Response) -> String { match response.outcome { Outcome::Screen(screen) => quasi_webview::Webview::new().screen(&screen), Outcome::Fragment { node, .. } => quasi_webview::Webview::new().fragment(&node), // Deliberately not a wildcard, for the reason the task tests give: a // redirect has no body, and a fallback returning empty markup would // read as a screen that rendered nothing. Outcome::Goto(action) => panic!("expected content, got a redirect to {action:?}"), // Same reasoning one step along: an overlay is a screen, but it is not // the screen this route was asked for. Rendering it here would let a // route that answered with the command palette pass as the page. Outcome::Over(_) => panic!("expected content, got a screen drawn over it"), Outcome::Anchored { .. } => { panic!("expected content, got a screen drawn at a point on it") } Outcome::Suggestions { field, .. } => { panic!("expected content, got a suggestion list for `{field}`") } Outcome::File { name, .. } => panic!("expected content, got the file `{name}`"), Outcome::Locate(_) => panic!("expected content, got a place on a map"), // `cb62a9dc`. Work that runs somewhere else and a region that says so: // not content, and not a place either. Outcome::Started { region, .. } => { panic!("expected content, got work started in `{region}`") } } } /// The review for this week. fn review(state: &AppState) -> String { html(get(state, "/weekly-review", Params::new())) } #[tokio::test] async fn the_review_carries_every_section_it_promises() { let state = state().await; let page = review(&state); for section in [ "Week at a Glance", "Accomplished", "Needs Attention", "Due This Week", // Escaped, because a heading is text and the renderer is the only thing // here that ever emits markup. "This Week's Focus", "Days Off", "Reflection", ] { assert!(page.contains(section), "missing section: {section}"); } } #[tokio::test] async fn an_overdue_row_says_its_project_as_well_as_when_it_slipped() { // `0c540c6f`. `Row::meta` sets rather than appends, so the project this row // placed first was thrown away by the due date placed second and no overdue // task ever named the project it belonged to. One joined string is the fix. let state = state().await; overdue_in_project(&state, "Write the thing", "Ledger"); let page = review(&state); assert!(page.contains("Ledger ยท 3d ago"), "{page}"); } #[tokio::test] async fn a_day_carries_its_counts_as_numbers_rather_than_as_dots() { // The first finding. `renderDayDots` caps completed at three dots and the // rest at two, which is an encoding running out of room rather than a fact // about the week. Seven rows, each saying what it actually holds. let state = state().await; for index in 0..5 { let task = add(&state, &format!("Done {index}")); state.tasks.complete(task.id, DESKTOP_USER_ID).unwrap(); } let page = review(&state); assert!(page.contains("5 done"), "the fifth completion survives"); // Seven days, whatever any of them holds. for day in ["Mon", "Sun"] { assert!(page.contains(day)); } } #[tokio::test] async fn the_focus_draws_three_named_places_and_the_empty_ones_are_reachable() { // The third finding, closed 2026-08-30 (quasicoherent `df57ed16`): three // slots always exist in the JS, and a place awaiting content is a region. // The meter that stood in for them is gone with the finding. let state = state().await; let task = add(&state, "The one thing"); post( &state, &format!("/weekly-review/focus/{}", task.id), Params::new().with("focus", "true"), ); let page = review(&state); assert!(page.contains("The one thing")); for slot in 1..=3 { assert!( page.contains(&format!(r#"id="weekly-focus-{slot}""#)), "priority {slot} is a place whether or not it is taken" ); assert!( page.contains(&format!(r#"aria-label="Priority {slot}""#)), "priority {slot} says which one it is" ); } // A `section` with a name, which is what makes an empty one reachable // rather than a nameless div nothing announces. assert!(page.contains(r#"
alert(1)"); post( &state, &format!("/weekly-review/focus/{}", task.id), Params::new().with("focus", "true"), ); let page = review(&state); assert!(!page.contains("