authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-05 00:19:46-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-05 15:36:02-07:00
log872eead565416e007ccbab614f0f9b3610fdbea8
treea5dfbb673f3e1d0c974eee18a714f6038e0a6339
parent8e3b7453999661e41945f9c6078d71c763ef2ec1
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

Read Shale issue status for standard accounts

Use the visible sidebar status instead of the repository-owner form. Assisted-by: gpt-6-sol

1 files changed, 15 insertions(+), 7 deletions(-)

dashboard/src/shale.rs+15-7
......@@ -297,10 +297,14 @@ fn issue(html: &str, repository: &str, id: Option<u64>) -> Result<Value> {
297297 .unwrap_or((None, None))
298298 } else { (None, None) }
299299 } else { (None, None) };
300 let status = document
301 .select(&Selector::parse("select[name=status] option[selected]").unwrap())
302 .next()
303 .and_then(|e| e.attr("value"));
300 let statuses: Vec<_> = document
301 .select(&Selector::parse("dl.sidebar dd span[class*='issuestatus-']").unwrap())
302 .filter_map(|status| status.value().classes().find_map(|class| class.strip_prefix("issuestatus-")))
303 .collect();
304 let status = match statuses.as_slice() {
305 [status] => Some(*status),
306 _ => None,
307 };
304308 if title.as_ref().is_none_or(|title| title.is_empty())
305309 || issue_id.is_none_or(|n| n == 0 || id.is_some_and(|id| n != id))
306310 || status.is_none()
......@@ -911,16 +915,20 @@ mod tests {
911915 }
912916 #[test]
913917 fn issue_pages_must_match_repository_identity_and_issue_number() {
914 let page = "<meta name='astheno.shale.repo.name' content='owned'><body id=page-issue><h1><span>#3</span><span>Snow &amp; ☃</span></h1><select name=status><option selected value=done>Done</option></select>";
918 let page = "<meta name='astheno.shale.repo.name' content='owned'><body id=page-issue><h1><span>#3</span><span>Snow &amp; ☃</span></h1><dl class=sidebar><dd><span class=issuestatus-done><span>Done</span></span></dd></dl>";
915919 assert_eq!(issue(page, "owned", Some(3)).unwrap()["title"], "Snow & ☃");
920 assert_eq!(issue(page, "owned", Some(3)).unwrap()["status"], "done");
921 let owner = format!("{page}<form><select name=status><option selected value=todo>Todo</option></select></form>");
922 assert_eq!(issue(&owner, "owned", Some(3)).unwrap()["status"], "done");
916923 assert!(issue(page, "other", Some(3)).is_err());
917924 assert!(issue(page, "owned", Some(4)).is_err());
918925 assert!(issue(&page.replace("page-issue", "page-login"), "owned", Some(3)).is_err());
919 assert!(issue(&page.replace("selected", ""), "owned", Some(3)).is_err());
926 assert!(issue(&page.replace("issuestatus-done", "unknown"), "owned", Some(3)).is_err());
927 assert!(issue(&format!("{page}<dl class=sidebar><dd><span class=issuestatus-todo></span></dd></dl>"), "owned", Some(3)).is_err());
920928 }
921929 #[test]
922930 fn verified_r1616_issue_heading_keeps_identity_checks() {
923 let page = "<head><meta name='astheno.shale.repo.name' content='owned'></head><body id=page-issue><h1>Issue #3: Snow &amp; ☃</h1><select name=status><option selected value=done>Done</option></select><footer class=usa-footer><div class=usa-footer__secondary-section><a href='https://astheno.software/shale/'>shale r1616-ga87d2f5.zig.0.16.0</a></div></footer></body>";
931 let page = "<head><meta name='astheno.shale.repo.name' content='owned'></head><body id=page-issue><h1>Issue #3: Snow &amp; ☃</h1><dl class=sidebar><dd><span class=issuestatus-done><span>Done</span></span></dd></dl><footer class=usa-footer><div class=usa-footer__secondary-section><a href='https://astheno.software/shale/'>shale r1616-ga87d2f5.zig.0.16.0</a></div></footer></body>";
924932 assert_eq!(issue(page, "owned", Some(3)).unwrap()["title"], "Snow & ☃");
925933 assert!(issue(page, "other", Some(3)).is_err());
926934 assert!(issue(page, "owned", Some(4)).is_err());