Skip to main content

max / goingson

5.3 KB · 136 lines History Blame Raw
1 //! The About section, driven through the router against a real state.
2 //!
3 //! The assertion worth reading is the last one. This section exists because two
4 //! host facts moved onto `AppState`, and the whole claim is that a route handler
5 //! can now answer them with nothing but `&AppState`. Every test here goes
6 //! through `router()` for that reason: calling `pane` directly would prove the
7 //! function works and say nothing about whether a request can reach it.
8
9 use std::sync::Arc;
10
11 use quasi_http::Serves as _;
12 use quasi_router::{Outcome, Params, Request};
13
14 use crate::quasi::router;
15 use crate::state::AppState;
16
17 async fn state() -> Arc<AppState> {
18 let (state, _) = crate::test_utils::setup_test_state().await;
19 state
20 }
21
22 fn html(state: &AppState, path: &str) -> String {
23 let response = router()
24 .handle(state, Request::get(path).carrying(Params::new()))
25 .expect("the route answers");
26 match &response.outcome {
27 Outcome::Screen(screen) => quasi_webview::Webview::new().screen(screen),
28 Outcome::Fragment { node, .. } => quasi_webview::Webview::new().fragment(node),
29 other => panic!("expected content, got {other:?}"),
30 }
31 }
32
33 #[tokio::test]
34 async fn about_is_a_section_of_settings() {
35 let state = state().await;
36 let markup = html(&state, "/settings/about");
37
38 assert!(markup.contains("About GoingsOn"), "{markup}");
39 // The sidebar offers it, which is the half that makes it reachable.
40 assert!(markup.contains("/settings/about"), "{markup}");
41 }
42
43 #[tokio::test]
44 async fn the_version_and_the_platform_come_off_the_state() {
45 // The two facts the old header called host-bound. They are on `AppState`
46 // now, resolved at startup, which is what `theme_dirs` did for Appearance.
47 let state = state().await;
48 let markup = html(&state, "/settings/about");
49
50 assert!(markup.contains("0.0.0-test"), "{markup}");
51 assert!(markup.contains("test"), "{markup}");
52 assert!(markup.contains("Make Creative, LLC"), "{markup}");
53 assert!(markup.contains("PolyForm Noncommercial 1.0.0"), "{markup}");
54 }
55
56 #[tokio::test]
57 async fn the_update_check_shows_the_default_before_anyone_has_chosen() {
58 // A first launch has no `preferences.json` at all, and the default is on.
59 // The section must not read that as off.
60 let state = state().await;
61 let markup = html(&state, "/settings/about");
62 assert!(markup.contains("update_check_on_launch"), "{markup}");
63
64 let prefs = super::read(&state);
65 assert!(prefs.update_check_on_launch, "the default is on");
66 }
67
68 #[tokio::test]
69 async fn turning_the_update_check_off_writes_the_file_the_launch_path_reads() {
70 // The point of the route: `preferences.json` is read before the database is
71 // open, so this is a real file rather than a `user_config` row, and the
72 // described handler reaches it through the directory `AppState` holds.
73 let state = state().await;
74
75 let response = router()
76 .handle(
77 &state,
78 Request::post("/settings/about/update-check")
79 .sending(Params::new().with(super::UPDATE_CHECK, "disabled")),
80 )
81 .expect("the route answers");
82
83 assert!(
84 matches!(response.outcome, Outcome::Fragment { .. }),
85 "the section is answered back, not a whole screen"
86 );
87
88 // Read through the same function the launch path uses, not through the
89 // response: the file is the fact.
90 let dir = state.config_dir.as_ref().expect("a test has a config dir");
91 let written = crate::commands::load_at(&crate::commands::path_in(dir));
92 assert!(!written.update_check_on_launch);
93
94 // And back on again, so the route is not one-way.
95 router()
96 .handle(
97 &state,
98 Request::post("/settings/about/update-check")
99 .sending(Params::new().with(super::UPDATE_CHECK, "enabled")),
100 )
101 .expect("the route answers");
102 let written = crate::commands::load_at(&crate::commands::path_in(dir));
103 assert!(written.update_check_on_launch);
104 }
105
106 #[tokio::test]
107 async fn the_biometric_row_is_not_offered_anywhere() {
108 // Pinned so it reads as a decision rather than an omission somebody closes.
109 // The row is offered only where the OS can answer the prompt, and that is a
110 // live question to a mobile platform rather than a fact a startup resolves.
111 let state = state().await;
112 let markup = html(&state, "/settings/about");
113 assert!(!markup.contains("require_biometric"), "{markup}");
114 assert!(!markup.contains("Require unlock"), "{markup}");
115 }
116
117 #[tokio::test]
118 async fn a_host_with_nowhere_to_write_says_so_rather_than_inventing_a_path() {
119 // `config_dir` is `None` when the host would not give one up. Showing the
120 // default and refusing the write is the honest pair; writing to a guessed
121 // path would be a preference that silently never comes back.
122 let (mut state, _) = crate::test_utils::setup_test_state_owned().await;
123 state.config_dir = None;
124 let state = Arc::new(state);
125
126 let markup = html(&state, "/settings/about");
127 assert!(markup.contains("About GoingsOn"), "the section still draws");
128
129 let refused = router().handle(
130 &state,
131 Request::post("/settings/about/update-check")
132 .sending(Params::new().with(super::UPDATE_CHECK, "disabled")),
133 );
134 assert!(refused.is_err(), "the write is refused");
135 }
136