package fa import ( "bytes" "strings" "testing" "github.com/PuerkitoBio/goquery" ) // syntheticUserHTML mirrors the real /user/{name}/ markup closely enough to // catch the bugs the fictitious old fixture hid: a logged-in viewer's avatar // in the site nav (which must NOT be picked as the profile avatar), the // `Label: value` stats strip, and the // watcher/watching counts that live in the section headers rather than the // stats box. const syntheticUserHTML = ` Viewer somefurry
Artist | Registered: 8 years ago
Unwatch

Welcome to my profile.

Stats

Views: 1,176
Submissions: 1,234
Favs: 567
Comments Earned: 85
Comments Made: 83
Journals: 12

Recent Watchers

Recently Watched

` func TestParseUser_Synthetic(t *testing.T) { doc, err := goquery.NewDocumentFromReader(strings.NewReader(syntheticUserHTML)) if err != nil { t.Fatalf("setup: %v", err) } u, err := parseUser("SomeFurry", doc) if err != nil { t.Fatalf("parseUser: %v", err) } if u.DisplayName != "SomeFurry" { t.Errorf("DisplayName = %q; want SomeFurry", u.DisplayName) } if u.Name != "somefurry" { t.Errorf("Name = %q; want somefurry (lowercased)", u.Name) } if u.AvatarURL != "https://a.furaffinity.net/123/somefurry.gif" { t.Errorf("AvatarURL = %q; want the profile owner's avatar, not the logged-in viewer's", u.AvatarURL) } if u.Stats.Submissions != 1234 { t.Errorf("Stats.Submissions = %d; want 1234", u.Stats.Submissions) } if u.Stats.Favorites != 567 { t.Errorf("Stats.Favorites = %d; want 567", u.Stats.Favorites) } if u.Stats.Views != 1176 { t.Errorf("Stats.Views = %d; want 1176", u.Stats.Views) } if u.Stats.Comments != 85 { t.Errorf("Stats.Comments = %d; want 85", u.Stats.Comments) } if u.Stats.Journals != 12 { t.Errorf("Stats.Journals = %d; want 12", u.Stats.Journals) } if u.Stats.Watchers != 89 { t.Errorf("Stats.Watchers = %d; want 89", u.Stats.Watchers) } if u.Stats.Watching != 10 { t.Errorf("Stats.Watching = %d; want 10", u.Stats.Watching) } if !u.Watched { t.Error("Watched = false; want true (page shows an Unwatch button)") } if !strings.Contains(u.BioText, "Welcome") { t.Errorf("BioText missing expected content: %q", u.BioText) } if u.Joined.Year() != 2018 { t.Errorf("Joined.Year = %d; want 2018", u.Joined.Year()) } } func TestParseUser_RealFixture(t *testing.T) { raw := loadFixture(t, "user.html") doc, err := goquery.NewDocumentFromReader(bytes.NewReader(raw)) if err != nil { t.Fatalf("read doc: %v", err) } u, err := parseUser("fixture", doc) if err != nil { t.Fatalf("parseUser(real): %v", err) } // Values below are read directly from testdata/html/user.html. They guard // against FA markup drift a zero/empty here means a selector went stale. if u.DisplayName != "SoXX-TheFennec" { t.Errorf("DisplayName = %q; want SoXX-TheFennec", u.DisplayName) } if u.AvatarURL != "https://a.furaffinity.net/1515442832/soxx-thefennec.gif" { t.Errorf("AvatarURL = %q; want the profile owner's avatar", u.AvatarURL) } if u.Stats.Submissions != 30 { t.Errorf("Stats.Submissions = %d; want 30", u.Stats.Submissions) } if u.Stats.Favorites != 180 { t.Errorf("Stats.Favorites = %d; want 180", u.Stats.Favorites) } if u.Stats.Views != 1184 { t.Errorf("Stats.Views = %d; want 1184", u.Stats.Views) } if u.Stats.Comments != 85 { t.Errorf("Stats.Comments = %d; want 85", u.Stats.Comments) } if u.Stats.Watchers != 50 { t.Errorf("Stats.Watchers = %d; want 50", u.Stats.Watchers) } if u.Stats.Watching != 315 { t.Errorf("Stats.Watching = %d; want 315", u.Stats.Watching) } if u.SiteBanner == nil { t.Fatal("SiteBanner = nil; want the default site banner populated") } if u.SiteBanner.IsCustom { t.Errorf("SiteBanner.IsCustom = true; want false (user.html shows FA's default site banner)") } if !strings.Contains(u.SiteBanner.ImageURL, "/media/banners/") { t.Errorf("SiteBanner.ImageURL = %q; want a /media/banners/ URL", u.SiteBanner.ImageURL) } } // syntheticUserWithCustomBannerHTML reproduces FA's markup for a // user who has uploaded their own profile banner via /controls/profilebanner/. // The image lives under /art// rather than /media/banners/, which is the // signal the parser uses to set IsCustom. const syntheticUserWithCustomBannerHTML = ` Profile Banner image somefurry
SomeFurry
` // TestParseUser_SiteBanner_RealFixture validates the custom-banner case // against a real captured profile (gillpanda, who has uploaded a profile // banner). The fixture is captured by the `fixtures` build-tagged refresh // test using FA_TEST_USER_WITH_BANNER; the test skips cleanly when absent. func TestParseUser_SiteBanner_RealFixture(t *testing.T) { raw := loadFixture(t, "user_with_banner.html") doc, err := goquery.NewDocumentFromReader(bytes.NewReader(raw)) if err != nil { t.Fatalf("read doc: %v", err) } u, err := parseUser("gillpanda", doc) if err != nil { t.Fatalf("parseUser(real): %v", err) } if u.SiteBanner == nil { t.Fatal("SiteBanner = nil; want a populated custom banner") } if !u.SiteBanner.IsCustom { t.Errorf("SiteBanner.IsCustom = false; want true (gillpanda has a custom banner)") } if !strings.Contains(u.SiteBanner.ImageURL, "/art/gillpanda/") { t.Errorf("SiteBanner.ImageURL = %q; want a /art/gillpanda/ URL", u.SiteBanner.ImageURL) } } func TestParseUser_SiteBanner_Custom(t *testing.T) { doc, err := goquery.NewDocumentFromReader(strings.NewReader(syntheticUserWithCustomBannerHTML)) if err != nil { t.Fatalf("setup: %v", err) } u, err := parseUser("SomeFurry", doc) if err != nil { t.Fatalf("parseUser: %v", err) } if u.SiteBanner == nil { t.Fatal("SiteBanner = nil; want the custom banner populated") } if !u.SiteBanner.IsCustom { t.Errorf("SiteBanner.IsCustom = false; want true (URL is under /art//)") } want := "https://d.furaffinity.net/art/somefurry/1716929854/profile_banner.jpg" if u.SiteBanner.ImageURL != want { t.Errorf("SiteBanner.ImageURL = %q; want %q", u.SiteBanner.ImageURL, want) } }