Extract pure logic out of RSX components into unit-testable handlers modules #9
2 changed files with 43 additions and 11 deletions
|
|
@ -1,4 +1,4 @@
|
|||
use super::handlers::{normalize_feed_url, subscribed_message};
|
||||
use super::handlers::{normalize_feed_url, resolve_submit_outcome, SubmitOutcome};
|
||||
use crate::api;
|
||||
use crate::components::button::{Button, ButtonVariant};
|
||||
use crate::components::input::Input;
|
||||
|
|
@ -20,13 +20,14 @@ pub fn SubscribeForm(on_subscribed: EventHandler<()>) -> Element {
|
|||
spawn(async move {
|
||||
submitting.set(true);
|
||||
status.set(None);
|
||||
match api::feeds::subscribe_feed(feed_url).await {
|
||||
Ok(count) => {
|
||||
status.set(Some(Ok(subscribed_message(count))));
|
||||
let result = api::feeds::subscribe_feed(feed_url).await;
|
||||
match resolve_submit_outcome(result.map_err(|err| err.to_string())) {
|
||||
SubmitOutcome::Success { message } => {
|
||||
status.set(Some(Ok(message)));
|
||||
url.set(String::new());
|
||||
on_subscribed.call(());
|
||||
}
|
||||
Err(err) => status.set(Some(Err(err.to_string()))),
|
||||
SubmitOutcome::Failure { message } => status.set(Some(Err(message))),
|
||||
}
|
||||
submitting.set(false);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,10 +10,31 @@ pub fn normalize_feed_url(input: &str) -> Option<String> {
|
|||
}
|
||||
|
||||
/// Success message shown after a feed subscription pulls in new articles.
|
||||
pub fn subscribed_message(article_count: usize) -> String {
|
||||
fn subscribed_message(article_count: usize) -> String {
|
||||
format!("Subscribed — pulled in {article_count} article(s).")
|
||||
}
|
||||
|
||||
/// What the form should show, and whether the URL input should be cleared,
|
||||
/// after a subscription attempt resolves.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum SubmitOutcome {
|
||||
/// The subscription succeeded: show `message` and clear the input.
|
||||
Success { message: String },
|
||||
/// The subscription failed: show `message` and leave the input as-is,
|
||||
/// so the reader can fix it up and retry.
|
||||
Failure { message: String },
|
||||
}
|
||||
|
||||
/// Maps a `subscribe_feed` result to what the form should do next.
|
||||
pub fn resolve_submit_outcome(result: Result<usize, String>) -> SubmitOutcome {
|
||||
match result {
|
||||
Ok(count) => SubmitOutcome::Success {
|
||||
message: subscribed_message(count),
|
||||
},
|
||||
Err(message) => SubmitOutcome::Failure { message },
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -33,14 +54,24 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn message_reports_the_article_count() {
|
||||
fn success_reports_the_article_count_and_clears_the_input() {
|
||||
assert_eq!(
|
||||
subscribed_message(0),
|
||||
"Subscribed — pulled in 0 article(s)."
|
||||
resolve_submit_outcome(Ok(3)),
|
||||
SubmitOutcome::Success {
|
||||
message: "Subscribed — pulled in 3 article(s).".to_string()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// A failed attempt should surface the error but leave the input alone
|
||||
/// so the reader doesn't have to retype the URL to fix it.
|
||||
#[test]
|
||||
fn failure_surfaces_the_error_message() {
|
||||
assert_eq!(
|
||||
subscribed_message(3),
|
||||
"Subscribed — pulled in 3 article(s)."
|
||||
resolve_submit_outcome(Err("feed unreachable".to_string())),
|
||||
SubmitOutcome::Failure {
|
||||
message: "feed unreachable".to_string()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue