Extract subscribe_form's submit-result mapping into a testable outcome
The submit closure mixed pure decision logic (what should the form show after subscribe_feed resolves) with Dioxus signal orchestration. Splits out resolve_submit_outcome(Result<usize, String>) -> SubmitOutcome, so the success/failure mapping is unit-tested without a Dioxus runtime; the component now just applies the resulting outcome to its signals. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
2ae3293c9d
commit
bd086b0288
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::api;
|
||||||
use crate::components::button::{Button, ButtonVariant};
|
use crate::components::button::{Button, ButtonVariant};
|
||||||
use crate::components::input::Input;
|
use crate::components::input::Input;
|
||||||
|
|
@ -20,13 +20,14 @@ pub fn SubscribeForm(on_subscribed: EventHandler<()>) -> Element {
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
submitting.set(true);
|
submitting.set(true);
|
||||||
status.set(None);
|
status.set(None);
|
||||||
match api::feeds::subscribe_feed(feed_url).await {
|
let result = api::feeds::subscribe_feed(feed_url).await;
|
||||||
Ok(count) => {
|
match resolve_submit_outcome(result.map_err(|err| err.to_string())) {
|
||||||
status.set(Some(Ok(subscribed_message(count))));
|
SubmitOutcome::Success { message } => {
|
||||||
|
status.set(Some(Ok(message)));
|
||||||
url.set(String::new());
|
url.set(String::new());
|
||||||
on_subscribed.call(());
|
on_subscribed.call(());
|
||||||
}
|
}
|
||||||
Err(err) => status.set(Some(Err(err.to_string()))),
|
SubmitOutcome::Failure { message } => status.set(Some(Err(message))),
|
||||||
}
|
}
|
||||||
submitting.set(false);
|
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.
|
/// 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).")
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -33,14 +54,24 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn message_reports_the_article_count() {
|
fn success_reports_the_article_count_and_clears_the_input() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
subscribed_message(0),
|
resolve_submit_outcome(Ok(3)),
|
||||||
"Subscribed — pulled in 0 article(s)."
|
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!(
|
assert_eq!(
|
||||||
subscribed_message(3),
|
resolve_submit_outcome(Err("feed unreachable".to_string())),
|
||||||
"Subscribed — pulled in 3 article(s)."
|
SubmitOutcome::Failure {
|
||||||
|
message: "feed unreachable".to_string()
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue