e2e/previewweb_test.go
161 lines · 7010 bytes
1package e2e
2
3import (
4 "net/url"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9)
10
11// Every markup form has a Preview button that renders the draft above
12// the textarea and writes nothing (#235). The forms are: issue and MR
13// create, their edit and comment boxes, release create and edit, the
14// profile about text, and the file editor on a path the forge renders.
15func TestMarkupPreviewWeb(t *testing.T) {
16 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
17 aliceKey := inst.newKey(t, "alice")
18 inst.admin(t, "admin", "user", "create", "alice",
19 "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
20 if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/app"); code != 0 {
21 t.Fatalf("repo create: %s", errOut)
22 }
23 env := inst.gitEnv(aliceKey)
24 work := t.TempDir()
25 mustGit(t, work, env, "clone", inst.sshURL("alice/app"), "w")
26 dir := filepath.Join(work, "w")
27 mustGit(t, dir, env, "checkout", "-q", "-b", "main")
28 os.WriteFile(filepath.Join(dir, "README.md"), []byte("# hi\n"), 0o644)
29 os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a\n"), 0o644)
30 mustGit(t, dir, env, "add", ".")
31 mustGit(t, dir, env, "commit", "-q", "-m", "base")
32 mustGit(t, dir, env, "push", "-q", "origin", "main")
33 mustGit(t, dir, env, "checkout", "-q", "-b", "topic")
34 os.WriteFile(filepath.Join(dir, "b.txt"), []byte("b\n"), 0o644)
35 mustGit(t, dir, env, "add", ".")
36 mustGit(t, dir, env, "commit", "-q", "-m", "topic")
37 mustGit(t, dir, env, "push", "-q", "origin", "topic")
38 mustGit(t, dir, env, "tag", "v1")
39 mustGit(t, dir, env, "push", "-q", "origin", "v1")
40
41 alice := inst.login(t, aliceKey)
42 base := inst.base() + "/alice/app"
43
44 post := func(where string, v url.Values) string {
45 t.Helper()
46 status, body := browserPost(t, alice, where, v)
47 if status != 200 {
48 t.Fatalf("post %s %v: %d", where, v, status)
49 }
50 return body
51 }
52 // previewed asserts the rendering is on the page and the draft came
53 // back with it.
54 previewed := func(what, body, want, kept string) {
55 t.Helper()
56 if !strings.Contains(body, `<p class="previewmark">Preview</p>`) {
57 t.Fatalf("%s: no preview block:\n%s", what, body)
58 }
59 if !strings.Contains(body, want) {
60 t.Fatalf("%s: preview does not contain %q:\n%s", what, want, body)
61 }
62 if kept != "" && !strings.Contains(body, kept) {
63 t.Fatalf("%s: form lost %q:\n%s", what, kept, body)
64 }
65 }
66
67 // Issue create: markdown renders, and the title and labels survive.
68 b := post(base+"/issues/new", url.Values{
69 "title": {"draft title"}, "body": {"# heading\n"}, "labels": {"bug"},
70 "format": {"md"}, "preview": {"1"},
71 })
72 previewed("issue create", b, "<h1", `value="draft title"`)
73 if !strings.Contains(b, `value="bug"`) {
74 t.Fatalf("issue create preview lost the labels:\n%s", b)
75 }
76 // Nothing was opened.
77 if out, _, _ := inst.ssh(t, aliceKey, "", "issue", "list", "alice/app", "--json"); strings.Contains(out, "draft title") {
78 t.Fatalf("preview created an issue: %s", out)
79 }
80
81 // The org choice previews as org, not markdown.
82 b = post(base+"/issues/new", url.Values{
83 "title": {"t"}, "body": {"Some /emphasis/ here.\n"}, "format": {"org"}, "preview": {"1"},
84 })
85 previewed("issue create org", b, "<em>emphasis</em>", `<option value="org" selected>`)
86
87 // A real issue, then its comment and edit boxes.
88 if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "create", "alice/app", "--title", "real", "--body", "body"); code != 0 {
89 t.Fatalf("issue create: %s", errOut)
90 }
91 b = post(base+"/issues/1/comment", url.Values{"body": {"a **bold** remark"}, "preview": {"1"}})
92 previewed("issue comment", b, "<strong>bold</strong>", "a **bold** remark")
93 b = post(base+"/issues/1/edit", url.Values{
94 "title": {"real"}, "body": {"## edited"}, "preview": {"1"},
95 })
96 previewed("issue edit", b, "<h2", "## edited")
97 if !strings.Contains(b, `<details class="editbox" open>`) {
98 t.Fatalf("issue edit preview left the box shut:\n%s", b)
99 }
100 // Neither wrote: one comment would show in the thread, an edit in the body.
101 if out, _, _ := inst.ssh(t, aliceKey, "", "issue", "show", "alice/app", "1", "--json"); strings.Contains(out, "bold") || strings.Contains(out, "edited") {
102 t.Fatalf("preview wrote to the issue: %s", out)
103 }
104
105 // MR create, comment and edit.
106 b = post(base+"/mrs/new", url.Values{
107 "source": {"topic"}, "target": {"main"}, "title": {"mr draft"},
108 "body": {"# mr heading"}, "format": {"md"}, "preview": {"1"},
109 })
110 previewed("mr create", b, "<h1", `value="mr draft"`)
111 if out, _, _ := inst.ssh(t, aliceKey, "", "mr", "list", "alice/app", "--json"); strings.Contains(out, "mr draft") {
112 t.Fatalf("preview opened a merge request: %s", out)
113 }
114 if _, errOut, code := inst.ssh(t, aliceKey, "", "mr", "create", "alice/app",
115 "--source", "topic", "--target", "main", "--title", "'real mr'"); code != 0 {
116 t.Fatalf("mr create: %s", errOut)
117 }
118 b = post(base+"/mrs/1/comment", url.Values{"body": {"a `code` remark"}, "preview": {"1"}})
119 previewed("mr comment", b, "<code>code</code>", "a `code` remark")
120 b = post(base+"/mrs/1/edit", url.Values{"title": {"real mr"}, "body": {"## mr edited"}, "preview": {"1"}})
121 previewed("mr edit", b, "<h2", "## mr edited")
122
123 // Release create, then edit.
124 b = post(base+"/releases", url.Values{
125 "tag": {"v1"}, "title": {"first"}, "notes": {"* a bullet"}, "preview": {"1"},
126 })
127 previewed("release create", b, "<li>", "* a bullet")
128 if out, _, _ := inst.ssh(t, aliceKey, "", "release", "list", "alice/app", "--json"); strings.Contains(out, "first") {
129 t.Fatalf("preview created a release: %s", out)
130 }
131 if _, errOut, code := inst.ssh(t, aliceKey, "", "release", "create", "alice/app", "v1", "--title", "first"); code != 0 {
132 t.Fatalf("release create: %s", errOut)
133 }
134 b = post(base+"/releases", url.Values{
135 "action": {"edit"}, "tag": {"v1"}, "title": {"first"},
136 "notes": {"## release notes"}, "preview": {"1"},
137 })
138 previewed("release edit", b, "<h2", "## release notes")
139
140 // The profile form takes no markup: the about text is a file, and the
141 // file editor below is what previews it.
142 if _, body := browserGet(t, alice, inst.base()+"/settings"); strings.Contains(body, `name="preview"`) {
143 t.Fatalf("the profile form offers a preview:\n%s", body)
144 }
145
146 // The file editor previews a rendered path and offers nothing on one
147 // it does not render.
148 b = post(base+"/edit/main/README.md", url.Values{
149 "content": {"# from the editor"}, "message": {"kept message"}, "preview": {"1"},
150 })
151 previewed("file editor", b, "from the editor", `value="kept message"`)
152 if _, body := browserGet(t, alice, base+"/blob/main/README.md"); strings.Contains(body, "from the editor") {
153 t.Fatalf("preview committed the file:\n%s", body)
154 }
155 if _, body := browserGet(t, alice, base+"/edit/main/a.txt"); strings.Contains(body, `name="preview"`) {
156 t.Fatalf("a plain text file offers a preview:\n%s", body)
157 }
158 if _, body := browserGet(t, alice, base+"/edit/main/README.md"); !strings.Contains(body, `name="preview"`) {
159 t.Fatalf("no preview button on a markdown file:\n%s", body)
160 }
161}