e2e/searchweb_test.go

v1.40.1
gitbay/e2e/searchweb_test.go history · blame · raw

123 lines · 5090 bytes

  1package e2e
  2
  3import (
  4	"net/url"
  5	"strings"
  6	"testing"
  7)
  8
  9// TestGlobalSearchAndNotificationsWeb covers the two web surfaces #118
 10// still lacked: /search across the instance, and the notification inbox.
 11func TestGlobalSearchAndNotificationsWeb(t *testing.T) {
 12	t.Parallel()
 13	inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
 14	aliceKey := inst.newKey(t, "alice")
 15	bobKey := inst.newKey(t, "bob")
 16	inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
 17	inst.admin(t, "admin", "user", "create", "bob", "--key", bobKey+".pub")
 18
 19	if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/widget"); code != 0 {
 20		t.Fatalf("repo create: %s", errOut)
 21	}
 22	if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/secret", "--private"); code != 0 {
 23		t.Fatalf("private repo create: %s", errOut)
 24	}
 25	if _, errOut, code := inst.ssh(t, bobKey, "", "issue", "create", "alice/widget",
 26		"--title", "'widget leaks memory'", "--body", "'it climbs'"); code != 0 {
 27		t.Fatalf("issue create: %s", errOut)
 28	}
 29	if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "create", "alice/secret",
 30		"--title", "'widget in the private repo'"); code != 0 {
 31		t.Fatalf("private issue create: %s", errOut)
 32	}
 33
 34	// Anonymous search reaches the public repository and its issue, and
 35	// neither the private repository nor its issue.
 36	status, body := inst.get(t, "/search?q=widget")
 37	if status != 200 {
 38		t.Fatalf("anonymous search: %d", status)
 39	}
 40	if !strings.Contains(body, `href="/alice/widget"`) ||
 41		!strings.Contains(body, "widget leaks memory") ||
 42		!strings.Contains(body, `href="/alice/widget/issues/1"`) {
 43		t.Fatalf("anonymous search missing public hits:\n%s", body)
 44	}
 45	if strings.Contains(body, "alice/secret") || strings.Contains(body, "private repo") {
 46		t.Fatal("anonymous search leaked a private repository")
 47	}
 48
 49	// Filtering by kind keeps the repository row and drops the issue.
 50	_, body = inst.get(t, "/search?q=widget&kind=repo")
 51	if !strings.Contains(body, `href="/alice/widget"`) || strings.Contains(body, "widget leaks memory") {
 52		t.Fatalf("kind=repo filter:\n%s", body)
 53	}
 54	_, body = inst.get(t, "/search?q=x")
 55	if !strings.Contains(body, "2 to 200 characters") {
 56		t.Fatal("short query not refused")
 57	}
 58
 59	// A query that matches nothing says what to do next instead of
 60	// repeating the count line's "no matches" under it (#248).
 61	_, body = inst.get(t, "/search?q=zzznothing&kind=issue")
 62	if !strings.Contains(body, "0 results") || !strings.Contains(body, "Try fewer words") ||
 63		!strings.Contains(body, "search everything") {
 64		t.Fatalf("empty search lacks its recovery line:\n%s", body)
 65	}
 66	if strings.Contains(body, "no matches") {
 67		t.Fatalf("empty search repeats the count line:\n%s", body)
 68	}
 69
 70	// Alice sees her private repository and its issue in the same page.
 71	browser := inst.login(t, aliceKey)
 72	status, body = browserGet(t, browser, inst.base()+"/search?q=widget")
 73	if status != 200 || !strings.Contains(body, "alice/secret") ||
 74		!strings.Contains(body, "widget in the private repo") {
 75		t.Fatalf("owner search misses private rows:\n%s", body)
 76	}
 77
 78	// The rail carries the unread badge and the notifications page lists
 79	// the issue bob opened.
 80	status, body = browserGet(t, browser, inst.base()+"/notifications")
 81	if status != 200 || !strings.Contains(body, "bob opened issue #1") ||
 82		!strings.Contains(body, `href="/alice/widget/issues/1"`) {
 83		t.Fatalf("notifications page:\n%s", body)
 84	}
 85	if !strings.Contains(body, `href="/notifications"`) {
 86		t.Fatal("rail has no notifications link")
 87	}
 88
 89	// Marking all read empties the unread list but not the full one.
 90	if status, _ := browserPost(t, browser, inst.base()+"/notifications", url.Values{}); status != 200 {
 91		t.Fatalf("mark all read: %d", status)
 92	}
 93	_, body = browserGet(t, browser, inst.base()+"/notifications")
 94	if !strings.Contains(body, "no unread notifications") {
 95		t.Fatalf("unread list after sweep:\n%s", body)
 96	}
 97	_, body = browserGet(t, browser, inst.base()+"/notifications?all=1")
 98	if !strings.Contains(body, "bob opened issue #1") {
 99		t.Fatalf("all list after sweep:\n%s", body)
100	}
101
102	// Watching from the repository header is the same state the CLI sets.
103	if status, _ := browserPost(t, browser, inst.base()+"/alice/widget/watch", url.Values{}); status != 200 {
104		t.Fatalf("watch toggle: %d", status)
105	}
106	out, errOut, code := inst.ssh(t, aliceKey, "", "notifications", "list", "--all", "--json")
107	if code != 0 {
108		t.Fatalf("notifications list: %s", errOut)
109	}
110	if !strings.Contains(out, "alice/widget") {
111		t.Fatalf("inbox over ssh: %s", out)
112	}
113	_, body = browserGet(t, browser, inst.base()+"/alice/widget")
114	if !strings.Contains(body, ">Watching</button>") {
115		t.Fatalf("repo header does not show the watch state:\n%s", body)
116	}
117
118	// Anonymous visitors get the search page; the inbox sends them to log
119	// in. http.Get follows the redirect, so the login page is the proof.
120	if status, body := inst.get(t, "/notifications"); status != 200 || !strings.Contains(body, "Sign in") {
121		t.Fatalf("anonymous /notifications: %d\n%s", status, body)
122	}
123}