//! The About section, driven through the router against a real state. //! //! The assertion worth reading is the last one. This section exists because two //! host facts moved onto `AppState`, and the whole claim is that a route handler //! can now answer them with nothing but `&AppState`. Every test here goes //! through `router()` for that reason: calling `pane` directly would prove the //! function works and say nothing about whether a request can reach it. use std::sync::Arc; use quasi_http::Serves as _; use quasi_router::{Outcome, Params, Request}; use crate::quasi::router; use crate::state::AppState; async fn state() -> Arc { let (state, _) = crate::test_utils::setup_test_state().await; state } fn html(state: &AppState, path: &str) -> String { let response = router() .handle(state, Request::get(path).carrying(Params::new())) .expect("the route answers"); match &response.outcome { Outcome::Screen(screen) => quasi_webview::Webview::new().screen(screen), Outcome::Fragment { node, .. } => quasi_webview::Webview::new().fragment(node), other => panic!("expected content, got {other:?}"), } } #[tokio::test] async fn about_is_a_section_of_settings() { let state = state().await; let markup = html(&state, "/settings/about"); assert!(markup.contains("About GoingsOn"), "{markup}"); // The sidebar offers it, which is the half that makes it reachable. assert!(markup.contains("/settings/about"), "{markup}"); } #[tokio::test] async fn the_version_and_the_platform_come_off_the_state() { // The two facts the old header called host-bound. They are on `AppState` // now, resolved at startup, which is what `theme_dirs` did for Appearance. let state = state().await; let markup = html(&state, "/settings/about"); assert!(markup.contains("0.0.0-test"), "{markup}"); assert!(markup.contains("test"), "{markup}"); assert!(markup.contains("Make Creative, LLC"), "{markup}"); assert!(markup.contains("PolyForm Noncommercial 1.0.0"), "{markup}"); } #[tokio::test] async fn the_update_check_shows_the_default_before_anyone_has_chosen() { // A first launch has no `preferences.json` at all, and the default is on. // The section must not read that as off. let state = state().await; let markup = html(&state, "/settings/about"); assert!(markup.contains("update_check_on_launch"), "{markup}"); let prefs = super::read(&state); assert!(prefs.update_check_on_launch, "the default is on"); } #[tokio::test] async fn turning_the_update_check_off_writes_the_file_the_launch_path_reads() { // The point of the route: `preferences.json` is read before the database is // open, so this is a real file rather than a `user_config` row, and the // described handler reaches it through the directory `AppState` holds. let state = state().await; let response = router() .handle( &state, Request::post("/settings/about/update-check") .sending(Params::new().with(super::UPDATE_CHECK, "disabled")), ) .expect("the route answers"); assert!( matches!(response.outcome, Outcome::Fragment { .. }), "the section is answered back, not a whole screen" ); // Read through the same function the launch path uses, not through the // response: the file is the fact. let dir = state.config_dir.as_ref().expect("a test has a config dir"); let written = crate::commands::load_at(&crate::commands::path_in(dir)); assert!(!written.update_check_on_launch); // And back on again, so the route is not one-way. router() .handle( &state, Request::post("/settings/about/update-check") .sending(Params::new().with(super::UPDATE_CHECK, "enabled")), ) .expect("the route answers"); let written = crate::commands::load_at(&crate::commands::path_in(dir)); assert!(written.update_check_on_launch); } #[tokio::test] async fn the_biometric_row_is_not_offered_anywhere() { // Pinned so it reads as a decision rather than an omission somebody closes. // The row is offered only where the OS can answer the prompt, and that is a // live question to a mobile platform rather than a fact a startup resolves. let state = state().await; let markup = html(&state, "/settings/about"); assert!(!markup.contains("require_biometric"), "{markup}"); assert!(!markup.contains("Require unlock"), "{markup}"); } #[tokio::test] async fn a_host_with_nowhere_to_write_says_so_rather_than_inventing_a_path() { // `config_dir` is `None` when the host would not give one up. Showing the // default and refusing the write is the honest pair; writing to a guessed // path would be a preference that silently never comes back. let (mut state, _) = crate::test_utils::setup_test_state_owned().await; state.config_dir = None; let state = Arc::new(state); let markup = html(&state, "/settings/about"); assert!(markup.contains("About GoingsOn"), "the section still draws"); let refused = router().handle( &state, Request::post("/settings/about/update-check") .sending(Params::new().with(super::UPDATE_CHECK, "disabled")), ); assert!(refused.is_err(), "the write is refused"); }