e2e/issueweb_test.go
91 lines · 3737 bytes
1package e2e
2
3import (
4 "net/url"
5 "strings"
6 "testing"
7)
8
9// TestIssueWebTriage closes, labels, assigns, and sets a milestone from
10// the browser. Each action runs the issue command the CLI runs, so the
11// CLI is the check that they took effect.
12func TestIssueWebTriage(t *testing.T) {
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",
17 "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
18 inst.admin(t, "admin", "user", "create", "bob",
19 "--key", bobKey+".pub", "--email", "bob@example.test", "--verified")
20
21 if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/app"); code != 0 {
22 t.Fatalf("repo create: %s", errOut)
23 }
24 if _, errOut, code := inst.ssh(t, aliceKey, "", "milestone", "create", "alice/app", "v1"); code != 0 {
25 t.Fatalf("milestone create: %s", errOut)
26 }
27 if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "create", "alice/app", "--title", "'needs triage'"); code != 0 {
28 t.Fatalf("issue create: %s", errOut)
29 }
30
31 alice := inst.login(t, aliceKey)
32 issueURL := inst.base() + "/alice/app/issues/1"
33
34 // The triage controls are on the page for someone with write access.
35 _, body := browserGet(t, alice, issueURL)
36 for _, want := range []string{`/issues/1/state`, `/issues/1/label`, `/issues/1/assign`, `value="v1"`} {
37 if !strings.Contains(body, want) {
38 t.Fatalf("issue page missing %q:\n%s", want, body)
39 }
40 }
41
42 // Label, assign, and milestone, each verified through the CLI.
43 if status, _ := browserPost(t, alice, issueURL+"/label", url.Values{"add": {"bug ui"}}); status != 200 {
44 t.Fatalf("label post: %d", status)
45 }
46 if status, _ := browserPost(t, alice, issueURL+"/assign", url.Values{"add": {"bob"}}); status != 200 {
47 t.Fatalf("assign post: %d", status)
48 }
49 if status, _ := browserPost(t, alice, issueURL+"/milestone", url.Values{"milestone": {"v1"}}); status != 200 {
50 t.Fatalf("milestone post: %d", status)
51 }
52 out, _, _ := inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "1", "--json")
53 for _, want := range []string{`"bug"`, `"ui"`, `"bob"`, `"v1"`} {
54 if !strings.Contains(out, want) {
55 t.Fatalf("triage did not land (%s):\n%s", want, out)
56 }
57 }
58
59 // Removing works the same way.
60 if status, _ := browserPost(t, alice, issueURL+"/label", url.Values{"remove": {"ui"}}); status != 200 {
61 t.Fatalf("label remove: %d", status)
62 }
63 out, _, _ = inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "1", "--json")
64 if strings.Contains(out, `"ui"`) {
65 t.Fatalf("label not removed:\n%s", out)
66 }
67
68 // Close, then reopen.
69 if status, _ := browserPost(t, alice, issueURL+"/state", url.Values{"action": {"close"}}); status != 200 {
70 t.Fatalf("close post: %d", status)
71 }
72 if out, _, _ := inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "1", "--json"); !strings.Contains(out, `"state":"closed"`) {
73 t.Fatalf("issue not closed:\n%s", out)
74 }
75 _, reopened := browserPost(t, alice, issueURL+"/state", url.Values{"action": {"reopen"}})
76 if !strings.Contains(reopened, "chip-open") {
77 t.Fatalf("issue not reopened:\n%s", reopened)
78 }
79
80 // A reader sees no controls and is refused if they forge the post.
81 carolKey := inst.newKey(t, "carol")
82 inst.admin(t, "admin", "user", "create", "carol", "--key", carolKey+".pub")
83 _, readerView := browserGet(t, inst.login(t, carolKey), issueURL)
84 if strings.Contains(readerView, `/issues/1/label`) {
85 t.Fatal("reader sees triage controls")
86 }
87 _, denied := browserPost(t, inst.login(t, carolKey), issueURL+"/state", url.Values{"action": {"close"}})
88 if !strings.Contains(denied, `class="error"`) {
89 t.Fatalf("reader closed someone else's issue:\n%s", denied)
90 }
91}