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