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