fabrica

hanna/fabrica

test(web): cover the signed-in dashboard activity view

243de22 · hanna committed on 2026-07-25

Add a login helper and assert the dashboard renders the contribution heatmap
and recent-activity feed for a signed-in viewer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
1 files changed · +56 −0UnifiedSplit
crates/web/src/tests.rs +56 −0
@@ -331,6 +331,62 @@ async fn explore_users_lists_the_directory() {
331331 assert!(html.contains("/avatar/ada"), "user avatar shown");
332332 }
333333
334+/// Sign in as `ada` and return the `Cookie` header value carrying the session.
335+async fn login(app: &Router) -> String {
336+ let form = app.clone().oneshot(get("/login")).await.unwrap();
337+ let csrf = form
338+ .headers()
339+ .get(header::SET_COOKIE)
340+ .unwrap()
341+ .to_str()
342+ .unwrap()
343+ .to_string();
344+ let token = csrf
345+ .split("fabrica_csrf=")
346+ .nth(1)
347+ .and_then(|s| s.split(';').next())
348+ .unwrap()
349+ .to_string();
350+ let req = Request::builder()
351+ .method("POST")
352+ .uri("/login")
353+ .header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
354+ .header(header::COOKIE, format!("fabrica_csrf={token}"))
355+ .body(Body::from(format!(
356+ "username=ada&password=secret12&_csrf={token}"
357+ )))
358+ .unwrap();
359+ let res = app.clone().oneshot(req).await.unwrap();
360+ let session = res
361+ .headers()
362+ .get(header::SET_COOKIE)
363+ .unwrap()
364+ .to_str()
365+ .unwrap();
366+ let session = session.split(';').next().unwrap();
367+ format!("fabrica_csrf={token}; {session}")
368+}
369+
370+#[tokio::test]
371+async fn dashboard_shows_activity_and_repos_when_signed_in() {
372+ let (_state, app) = app().await;
373+ let cookie = login(&app).await;
374+ let req = Request::builder()
375+ .uri("/")
376+ .header(header::COOKIE, cookie)
377+ .body(Body::empty())
378+ .unwrap();
379+ let res = app.oneshot(req).await.unwrap();
380+ assert_eq!(res.status(), StatusCode::OK);
381+ let html = body_string(res).await;
382+ assert!(
383+ html.contains("contributions in the last year"),
384+ "activity heatmap caption present"
385+ );
386+ assert!(html.contains("Recent activity"), "activity feed present");
387+ assert!(html.contains("class=\"heatmap\""), "heatmap svg present");
388+}
389+
334390 #[tokio::test]
335391 async fn settings_requires_authentication() {
336392 let (_state, app) = app().await;