Commit 584dc8d6ee
Verified · cmc
Layout: unified · split
internal/httpd/buildpages_test.go +63
| @@ -57,6 +57,69 @@ func TestBuildsPageRendersCommandOutput(t *testing.T) { | |||
| 57 | } | 57 | } |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | // A writer sees the instruction to add CI; a reader without push access | ||
| 61 | // sees only the fact, since the instruction is not theirs to act on | ||
| 62 | // (#270). | ||
| 63 | func TestBuildsEmptyStateOmitsInstructionForReaders(t *testing.T) { | ||
| 64 | var sb strings.Builder | ||
| 65 | filter := buildFilter{} | ||
| 66 | err := web.Render(&sb, "builds.html", struct { | ||
| 67 | repoPage | ||
| 68 | Builds []control.BuildOut | ||
| 69 | Jobs []control.JobOut | ||
| 70 | Runs []buildRun | ||
| 71 | Filter buildFilter | ||
| 72 | Facets []facetGroup | ||
| 73 | Refs []string | ||
| 74 | Older string | ||
| 75 | CanWrite bool | ||
| 76 | Notice string | ||
| 77 | }{ | ||
| 78 | testRepoPage(), nil, nil, nil, filter, nil, nil, "", false, "", | ||
| 79 | }) | ||
| 80 | if err != nil { | ||
| 81 | t.Fatalf("render: %v", err) | ||
| 82 | } | ||
| 83 | out := sb.String() | ||
| 84 | if !strings.Contains(out, "no builds") { | ||
| 85 | t.Error(`missing "no builds"`) | ||
| 86 | } | ||
| 87 | if strings.Contains(out, "ci.yml") { | ||
| 88 | t.Error("a reader without push access should not see the push instruction") | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | // A writer with zero builds gets the one instruction the page can give | ||
| 93 | // them (#270). | ||
| 94 | func TestBuildsEmptyStateShowsInstructionForWriters(t *testing.T) { | ||
| 95 | var sb strings.Builder | ||
| 96 | filter := buildFilter{} | ||
| 97 | err := web.Render(&sb, "builds.html", struct { | ||
| 98 | repoPage | ||
| 99 | Builds []control.BuildOut | ||
| 100 | Jobs []control.JobOut | ||
| 101 | Runs []buildRun | ||
| 102 | Filter buildFilter | ||
| 103 | Facets []facetGroup | ||
| 104 | Refs []string | ||
| 105 | Older string | ||
| 106 | CanWrite bool | ||
| 107 | Notice string | ||
| 108 | }{ | ||
| 109 | testRepoPage(), nil, nil, nil, filter, nil, nil, "", true, "", | ||
| 110 | }) | ||
| 111 | if err != nil { | ||
| 112 | t.Fatalf("render: %v", err) | ||
| 113 | } | ||
| 114 | out := sb.String() | ||
| 115 | if !strings.Contains(out, "no builds") { | ||
| 116 | t.Error(`missing "no builds"`) | ||
| 117 | } | ||
| 118 | if !strings.Contains(out, "push a commit with a .gitbay/ci.yml") { | ||
| 119 | t.Error("a writer with no builds should see the push instruction") | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 60 | func TestBuildPageRendersCommandOutput(t *testing.T) { | 123 | func TestBuildPageRendersCommandOutput(t *testing.T) { |
| 61 | var sb strings.Builder | 124 | var sb strings.Builder |
| 62 | err := web.Render(&sb, "build.html", buildView{ | 125 | err := web.Render(&sb, "build.html", buildView{ |
internal/httpd/mrpage_test.go +60
| @@ -83,6 +83,66 @@ func testMR(state string) store.MR { | |||
| 83 | HeadSHA: "ff6271a9d4570cd46f169091637a9d2e40ad5c2b"} | 83 | HeadSHA: "ff6271a9d4570cd46f169091637a9d2e40ad5c2b"} |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | // A finished merge request states an empty label list as a fact, not a | ||
| 87 | // promise something is still coming (#270). | ||
| 88 | func TestMRPageLabelsNoYetOnFinishedState(t *testing.T) { | ||
| 89 | var sb strings.Builder | ||
| 90 | if err := web.Render(&sb, "mr.html", mrPageData{ | ||
| 91 | repoPage: testRepoPage(), MR: testMR("merged"), View: "conversation", | ||
| 92 | }); err != nil { | ||
| 93 | t.Fatalf("render: %v", err) | ||
| 94 | } | ||
| 95 | if !strings.Contains(sb.String(), "no labels") { | ||
| 96 | t.Error(`merged MR with no labels should read "no labels", not "none yet"`) | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | // An open MR can still gain labels, so its empty state keeps "none yet" | ||
| 101 | // rather than the finished-item wording (#270). | ||
| 102 | func TestMRPageLabelsNoneYetOnOpenState(t *testing.T) { | ||
| 103 | var sb strings.Builder | ||
| 104 | if err := web.Render(&sb, "mr.html", mrPageData{ | ||
| 105 | repoPage: testRepoPage(), MR: testMR("open"), View: "conversation", | ||
| 106 | }); err != nil { | ||
| 107 | t.Fatalf("render: %v", err) | ||
| 108 | } | ||
| 109 | out := sb.String() | ||
| 110 | if !strings.Contains(out, "none yet") { | ||
| 111 | t.Error(`open MR with no labels should read "none yet"`) | ||
| 112 | } | ||
| 113 | if strings.Contains(out, "no labels") { | ||
| 114 | t.Error(`open MR with no labels should not read "no labels"`) | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | // A closed MR is finished the same way a merged one is (#270). | ||
| 119 | func TestMRPageLabelsNoLabelsOnClosedState(t *testing.T) { | ||
| 120 | var sb strings.Builder | ||
| 121 | if err := web.Render(&sb, "mr.html", mrPageData{ | ||
| 122 | repoPage: testRepoPage(), MR: testMR("closed"), View: "conversation", | ||
| 123 | }); err != nil { | ||
| 124 | t.Fatalf("render: %v", err) | ||
| 125 | } | ||
| 126 | if !strings.Contains(sb.String(), "no labels") { | ||
| 127 | t.Error(`closed MR with no labels should read "no labels", not "none yet"`) | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | func TestMRPageReviewersEmptyStateDropsNobody(t *testing.T) { | ||
| 132 | var sb strings.Builder | ||
| 133 | if err := web.Render(&sb, "mr.html", mrPageData{ | ||
| 134 | repoPage: testRepoPage(), MR: testMR("open"), View: "conversation", | ||
| 135 | }); err != nil { | ||
| 136 | t.Fatalf("render: %v", err) | ||
| 137 | } | ||
| 138 | if strings.Contains(sb.String(), "nobody yet") { | ||
| 139 | t.Error(`reviewers empty state should read "no reviewers"`) | ||
| 140 | } | ||
| 141 | if !strings.Contains(sb.String(), "no reviewers") { | ||
| 142 | t.Error(`missing "no reviewers"`) | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 86 | // The header states what happened to the MR: who opened, merged, or closed | 146 | // The header states what happened to the MR: who opened, merged, or closed |
| 87 | // it, and when. A merge or close with no stamp (imports, pre-0029 merges) | 147 | // it, and when. A merge or close with no stamp (imports, pre-0029 merges) |
| 88 | // names the state without claiming a byline it cannot back up. | 148 | // names the state without claiming a byline it cannot back up. |
internal/web/templates/builds.html +1 −1
| @@ -50,7 +50,7 @@ | |||
| 50 | {{range .Builds}}<a class="chip check-{{.Status}}" href="/{{$.Repo.OwnerName}}/{{$.Repo.Name}}/builds/{{.Number}}">{{.Job}}</a>{{end}} | 50 | {{range .Builds}}<a class="chip check-{{.Status}}" href="/{{$.Repo.OwnerName}}/{{$.Repo.Name}}/builds/{{.Number}}">{{.Job}}</a>{{end}} |
| 51 | </div> | 51 | </div> |
| 52 | </li> | 52 | </li> |
| 53 | {{else}}<li class="empty">no builds — push a commit with a <code>.gitbay/ci.yml</code></li>{{end}} | 53 | {{else}}<li class="empty">no builds{{if .CanWrite}} — push a commit with a .gitbay/ci.yml{{end}}</li>{{end}} |
| 54 | </ul> | 54 | </ul> |
| 55 | {{if .Older}}<p class="pager"><a href="{{.Older}}">older →</a></p>{{end}} | 55 | {{if .Older}}<p class="pager"><a href="{{.Older}}">older →</a></p>{{end}} |
| 56 | </div> | 56 | </div> |
internal/web/templates/dashboard.html +1 −1
| @@ -26,7 +26,7 @@ | |||
| 26 | <ul class="pins"> | 26 | <ul class="pins"> |
| 27 | {{range .Pins}}<li><a href="/{{.Owner}}/{{.Name}}"><span class="owner">{{.Owner}}/</span>{{.Name}}</a><span class="n" title="{{.Issues}} open issue{{if ne .Issues 1}}s{{end}}, {{.MRs}} open merge request{{if ne .MRs 1}}s{{end}}{{with .Build}}, last build {{.}}{{end}}"><b{{if .Issues}} class="wants"{{end}}>{{.Issues}} <span class="vh">open issues</span></b> <b{{if .MRs}} class="wants"{{end}}>{{.MRs}} <span class="vh">open merge requests</span></b> <span class="dot{{if eq .Build "success"}} ok{{else if or (eq .Build "failure") (eq .Build "error")}} bad{{else if or (eq .Build "pending") (eq .Build "running")}} pend{{end}}"><span class="vh">{{with .Build}}last build {{.}}{{else}}no builds{{end}}</span></span></span></li> | 27 | {{range .Pins}}<li><a href="/{{.Owner}}/{{.Name}}"><span class="owner">{{.Owner}}/</span>{{.Name}}</a><span class="n" title="{{.Issues}} open issue{{if ne .Issues 1}}s{{end}}, {{.MRs}} open merge request{{if ne .MRs 1}}s{{end}}{{with .Build}}, last build {{.}}{{end}}"><b{{if .Issues}} class="wants"{{end}}>{{.Issues}} <span class="vh">open issues</span></b> <b{{if .MRs}} class="wants"{{end}}>{{.MRs}} <span class="vh">open merge requests</span></b> <span class="dot{{if eq .Build "success"}} ok{{else if or (eq .Build "failure") (eq .Build "error")}} bad{{else if or (eq .Build "pending") (eq .Build "running")}} pend{{end}}"><span class="vh">{{with .Build}}last build {{.}}{{else}}no builds{{end}}</span></span></span></li> |
| 28 | {{end}}</ul> | 28 | {{end}}</ul> |
| 29 | {{else}}<p class="none">Nothing pinned yet. Press Pin on a repository.</p>{{end}} | 29 | {{else}}<p class="none">nothing pinned — press Pin on a repository you visit</p>{{end}} |
| 30 | </aside> | 30 | </aside> |
| 31 | 31 | ||
| 32 | <section class="dashmain"> | 32 | <section class="dashmain"> |
internal/web/templates/mr.html +2 −2
| @@ -144,7 +144,7 @@ | |||
| 144 | <div class="grp"> | 144 | <div class="grp"> |
| 145 | <h2>Reviewers</h2> | 145 | <h2>Reviewers</h2> |
| 146 | {{if .MR.ReviewRequests}}<p class="row">{{range .MR.ReviewRequests}}<a href="/{{.}}">{{.}}</a> {{end}}</p> | 146 | {{if .MR.ReviewRequests}}<p class="row">{{range .MR.ReviewRequests}}<a href="/{{.}}">{{.}}</a> {{end}}</p> |
| 147 | {{else}}<p class="none">nobody yet</p>{{end}} | 147 | {{else}}<p class="none">no reviewers</p>{{end}} |
| 148 | {{if and .CanWrite (or (eq .MR.State "open") (eq .MR.State "source_gone"))}} | 148 | {{if and .CanWrite (or (eq .MR.State "open") (eq .MR.State "source_gone"))}} |
| 149 | <form method="post" action="{{$base}}/review-request" class="actions"> | 149 | <form method="post" action="{{$base}}/review-request" class="actions"> |
| 150 | <input type="text" name="add" aria-label="Add reviewers" autocomplete="off" spellcheck="false" placeholder="add, space-separated"> | 150 | <input type="text" name="add" aria-label="Add reviewers" autocomplete="off" spellcheck="false" placeholder="add, space-separated"> |
| @@ -175,7 +175,7 @@ | |||
| 175 | <div class="grp"> | 175 | <div class="grp"> |
| 176 | <h2>Labels</h2> | 176 | <h2>Labels</h2> |
| 177 | {{if .MR.Labels}}<p class="row">{{range .MR.Labels}}<a class="chip label" style="{{index $.LabelColors .}}" href="/{{$.Repo.OwnerName}}/{{$.Repo.Name}}/mrs?label={{.}}">{{.}}</a> {{end}}</p> | 177 | {{if .MR.Labels}}<p class="row">{{range .MR.Labels}}<a class="chip label" style="{{index $.LabelColors .}}" href="/{{$.Repo.OwnerName}}/{{$.Repo.Name}}/mrs?label={{.}}">{{.}}</a> {{end}}</p> |
| 178 | {{else}}<p class="none">none yet</p>{{end}} | 178 | {{else}}<p class="none">{{if or (eq .MR.State "merged") (eq .MR.State "closed")}}no labels{{else}}none yet{{end}}</p>{{end}} |
| 179 | {{if .CanWrite}} | 179 | {{if .CanWrite}} |
| 180 | <form method="post" action="{{$base}}/label" class="actions"> | 180 | <form method="post" action="{{$base}}/label" class="actions"> |
| 181 | <input type="text" name="add" aria-label="Add labels" autocomplete="off" spellcheck="false" placeholder="add, space-separated"> | 181 | <input type="text" name="add" aria-label="Add labels" autocomplete="off" spellcheck="false" placeholder="add, space-separated"> |
internal/web/templates/mrs.html +1 −1
| @@ -26,7 +26,7 @@ | |||
| 26 | </div> | 26 | </div> |
| 27 | </li> | 27 | </li> |
| 28 | {{else}}{{if .Query}}<li class="empty">no {{if ne .State "all"}}{{.State}} {{end}}merge requests matching “{{.Query}}”</li> | 28 | {{else}}{{if .Query}}<li class="empty">no {{if ne .State "all"}}{{.State}} {{end}}merge requests matching “{{.Query}}”</li> |
| 29 | {{else}}<li class="empty">no {{if ne .State "all"}}{{.State}} {{end}}merge requests — open one with <code>gitbay mr create {{.Repo.OwnerName}}/{{.Repo.Name}} --source ... --target {{.Repo.DefaultBranch}}</code></li>{{end}}{{end}} | 29 | {{else}}<li class="empty">no {{if ne .State "all"}}{{.State}} {{end}}merge requests</li>{{end}}{{end}} |
| 30 | </ul> | 30 | </ul> |
| 31 | {{if .Older}}<p class="pager"><a href="{{.Older}}">older →</a></p>{{end}} | 31 | {{if .Older}}<p class="pager"><a href="{{.Older}}">older →</a></p>{{end}} |
| 32 | </div> | 32 | </div> |
internal/web/templates/notifications.html +1 −1
| @@ -18,6 +18,6 @@ | |||
| 18 | </div> | 18 | </div> |
| 19 | {{if not .Read}}<form method="post" action="/notifications" class="inline"><input type="hidden" name="id" value="{{.ID}}"><button type="submit" class="btn">Mark read</button></form>{{end}} | 19 | {{if not .Read}}<form method="post" action="/notifications" class="inline"><input type="hidden" name="id" value="{{.ID}}"><button type="submit" class="btn">Mark read</button></form>{{end}} |
| 20 | </li> | 20 | </li> |
| 21 | {{else}}<li class="empty">{{if .All}}nothing here yet{{else}}nothing unread — <a href="/notifications?all=1">show all</a>{{end}}</li>{{end}} | 21 | {{else}}<li class="empty">{{if .All}}nothing to show{{else}}no unread notifications — <a href="/notifications?all=1">show all</a>{{end}}</li>{{end}} |
| 22 | </ul> | 22 | </ul> |
| 23 | {{end}} | 23 | {{end}} |