internal/httpd/reactions_test.go
106 lines · 3494 bytes
1package httpd
2
3import (
4 "net/http"
5 "net/http/httptest"
6 "net/url"
7 "strings"
8 "testing"
9
10 "gitbay.org/gitbay/internal/config"
11 "gitbay.org/gitbay/internal/store"
12)
13
14func reactServer(t *testing.T) (*Server, *store.Store, store.User, store.User) {
15 t.Helper()
16 st, err := store.Open(":memory:")
17 if err != nil {
18 t.Fatal(err)
19 }
20 t.Cleanup(func() { st.Close() })
21 if err := st.MigrateUp(); err != nil {
22 t.Fatal(err)
23 }
24 aid, _ := st.CreateUser("alice", false)
25 bid, _ := st.CreateUser("bob", false)
26 rid, err := st.CreateRepo("user", aid, "app", "public")
27 if err != nil {
28 t.Fatal(err)
29 }
30 n, _ := st.CreateIssue(rid, aid, "bug", "body", "md")
31 iss, _ := st.IssueByNumber(rid, n)
32 st.AddIssueComment(iss.ID, aid, "a comment", "md")
33 cfg := config.Default()
34 cfg.Web.Mode = "accounts"
35 return New(cfg, st, nil), st, store.User{ID: aid, Username: "alice"}, store.User{ID: bid, Username: "bob"}
36}
37
38func issueReq(method, target string, form url.Values) *http.Request {
39 var r *http.Request
40 if form != nil {
41 r = httptest.NewRequest(method, target, strings.NewReader(form.Encode()))
42 r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
43 } else {
44 r = httptest.NewRequest(method, target, nil)
45 }
46 r.SetPathValue("owner", "alice")
47 r.SetPathValue("repo", "app")
48 r.SetPathValue("n", "1")
49 return r
50}
51
52func TestIssueReactionsOnPage(t *testing.T) {
53 s, st, _, bob := reactServer(t)
54
55 // Bob reacts to the body and to the comment through the form.
56 for _, form := range []url.Values{
57 {"add": {"+1"}},
58 {"add": {"rocket"}, "comment": {"1"}},
59 } {
60 rr := httptest.NewRecorder()
61 s.issueReactSubmit(rr, issueReq("POST", "/alice/app/issues/1/react", form), bob)
62 if rr.Code != http.StatusSeeOther {
63 t.Fatalf("%v: status %d: %s", form, rr.Code, rr.Body)
64 }
65 }
66
67 // Signed in: a button for every reaction, his own marked pressed.
68 req := issueReq("GET", "/alice/app/issues/1", nil)
69 req.AddCookie(sessionCookieFor(t, s, st, bob.ID))
70 rr := httptest.NewRecorder()
71 s.issue(rr, req)
72 body := rr.Body.String()
73 if n := strings.Count(body, `class="react"`) + strings.Count(body, `class="react mine"`); n != 16 {
74 t.Errorf("%d reaction buttons, want 16 (8 on the body, 8 on the comment)", n)
75 }
76 if !strings.Contains(body, `name="remove" value="+1" class="react mine" aria-pressed="true"`) {
77 t.Errorf("own reaction not marked:\n%s", body)
78 }
79 if !strings.Contains(body, `name="add" value="eyes"`) || !strings.Contains(body, `<input type="hidden" name="comment" value="1">`) {
80 t.Error("add buttons or comment field missing")
81 }
82
83 // Signed out: counts only, no forms or buttons.
84 rr = httptest.NewRecorder()
85 s.issue(rr, issueReq("GET", "/alice/app/issues/1", nil))
86 body = rr.Body.String()
87 if strings.Contains(body, `class="react"`) && strings.Contains(body, "<button type=\"submit\" name=\"add\"") {
88 t.Error("buttons shown to a signed-out viewer")
89 }
90 if strings.Contains(body, `/react"`) {
91 t.Error("react form shown to a signed-out viewer")
92 }
93 if n := strings.Count(body, `<span class="react"`); n != 2 {
94 t.Errorf("%d counts, want 2:\n%s", n, body)
95 }
96 if !strings.Contains(body, "👍 1") || !strings.Contains(body, "🚀 1") {
97 t.Error("counts missing")
98 }
99
100 // Pressing a pressed button takes it back.
101 rr = httptest.NewRecorder()
102 s.issueReactSubmit(rr, issueReq("POST", "/alice/app/issues/1/react", url.Values{"remove": {"+1"}}), bob)
103 if got, _ := st.ReactionCounts("issue", 1, bob.ID); len(got[0]) != 0 {
104 t.Errorf("reaction not removed: %+v", got)
105 }
106}