Commit 09bc1d3bb9
Verified · cmc
internal/httpd/buildpages_test.go +2 −1
| @@ -35,11 +35,12 @@ func TestBuildsPageRendersCommandOutput(t *testing.T) { | ||
| 35 | 35 | Runs []buildRun |
| 36 | 36 | Filter buildFilter |
| 37 | 37 | FilterLinks []buildFilterLink |
| 38 | Facets []facetGroup | |
| 38 | 39 | Refs []string |
| 39 | 40 | CanWrite bool |
| 40 | 41 | Notice string |
| 41 | 42 | }{ |
| 42 | testRepoPage(), builds, jobs, groupRuns(builds), filter, filterLinks(filter, jobs), | |
| 43 | testRepoPage(), builds, jobs, groupRuns(builds), filter, filterLinks(filter, jobs), nil, | |
| 43 | 44 | distinctRefs(builds, filter.Ref), true, "", |
| 44 | 45 | }) |
| 45 | 46 | if err != nil { |
internal/httpd/builds.go +32 −1
| @@ -60,6 +60,33 @@ func filterLinks(f buildFilter, jobs []control.JobOut) []buildFilterLink { | ||
| 60 | 60 | return links |
| 61 | 61 | } |
| 62 | 62 | |
| 63 | // buildFacets is the builds page's side column: filterLinks' rows split | |
| 64 | // into their groups, plus one link per branch seen, which keeps status | |
| 65 | // and job and clears itself when active. | |
| 66 | func buildFacets(f buildFilter, jobs []control.JobOut, refs []string) []facetGroup { | |
| 67 | links := filterLinks(f, jobs) | |
| 68 | n := 1 + len(buildStatuses) | |
| 69 | status := facetGroup{Title: "Status"} | |
| 70 | for _, l := range links[:n] { | |
| 71 | status.Items = append(status.Items, facetItem{Label: l.Label, Href: l.Href, Active: l.Active}) | |
| 72 | } | |
| 73 | job := facetGroup{Title: "Jobs"} | |
| 74 | for _, l := range links[n:] { | |
| 75 | job.Items = append(job.Items, facetItem{Label: l.Label, Href: l.Href, Active: l.Active}) | |
| 76 | } | |
| 77 | branch := facetGroup{Title: "Branches"} | |
| 78 | base := url.Values{"ref": {f.Ref}, "status": {f.Status}, "job": {f.Job}} | |
| 79 | for _, ref := range refs { | |
| 80 | active := ref == f.Ref | |
| 81 | href := facetHref(base, "ref", ref) | |
| 82 | if active { | |
| 83 | href = facetHref(base, "ref", "") | |
| 84 | } | |
| 85 | branch.Items = append(branch.Items, facetItem{Label: ref, Href: href, Active: active}) | |
| 86 | } | |
| 87 | return []facetGroup{status, job, branch} | |
| 88 | } | |
| 89 | ||
| 63 | 90 | // distinctRefs lists each ref among builds once, in order, plus the |
| 64 | 91 | // current filter value if it is not already there. It backs the branch |
| 65 | 92 | // field's <datalist> suggestions, not a claim about what branches exist: |
| @@ -176,6 +203,8 @@ func (s *Server) builds(w http.ResponseWriter, r *http.Request) { | ||
| 176 | 203 | var jobs []control.JobOut |
| 177 | 204 | s.runControlInto(viewer, []string{"build", "jobs", p.Repo.Path()}, &jobs) |
| 178 | 205 | |
| 206 | refs := distinctRefs(builds, filter.Ref) | |
| 207 | ||
| 179 | 208 | s.render(w, "builds.html", struct { |
| 180 | 209 | repoPage |
| 181 | 210 | Builds []control.BuildOut |
| @@ -183,10 +212,12 @@ func (s *Server) builds(w http.ResponseWriter, r *http.Request) { | ||
| 183 | 212 | Runs []buildRun |
| 184 | 213 | Filter buildFilter |
| 185 | 214 | FilterLinks []buildFilterLink |
| 215 | Facets []facetGroup | |
| 186 | 216 | Refs []string |
| 187 | 217 | CanWrite bool |
| 188 | 218 | Notice string |
| 189 | }{p, builds, jobs, groupRuns(builds), filter, filterLinks(filter, jobs), distinctRefs(builds, filter.Ref), | |
| 219 | }{p, builds, jobs, groupRuns(builds), filter, filterLinks(filter, jobs), | |
| 220 | buildFacets(filter, jobs, refs), refs, | |
| 190 | 221 | s.canWriteRepo(r, p.Repo), s.takeFlash(w, r)}) |
| 191 | 222 | } |
| 192 | 223 | |
internal/httpd/builds_test.go +25
| @@ -149,3 +149,28 @@ func TestDistinctRefsNoDuplicateWhenCurrentAlreadyPresent(t *testing.T) { | ||
| 149 | 149 | t.Errorf("distinctRefs = %v, want %v", got, want) |
| 150 | 150 | } |
| 151 | 151 | } |
| 152 | ||
| 153 | // The builds column groups the same links filterLinks makes: "all" and | |
| 154 | // the statuses, then the jobs, then the branches seen (desktop layout spec). | |
| 155 | func TestBuildFacetsGroups(t *testing.T) { | |
| 156 | f := buildFilter{Ref: "main", Status: "success"} | |
| 157 | groups := buildFacets(f, []control.JobOut{{Name: "lint"}}, []string{"main", "dev"}) | |
| 158 | if len(groups) != 3 || groups[0].Title != "Status" || groups[1].Title != "Jobs" || groups[2].Title != "Branches" { | |
| 159 | t.Fatalf("groups: %+v", groups) | |
| 160 | } | |
| 161 | if groups[0].Items[0].Label != "all" || groups[0].Items[0].Href != "?ref=main" || groups[0].Items[0].Active { | |
| 162 | t.Errorf("all: %+v", groups[0].Items[0]) | |
| 163 | } | |
| 164 | if s := groups[0].Items[3]; s.Label != "success" || !s.Active { | |
| 165 | t.Errorf("success: %+v", s) | |
| 166 | } | |
| 167 | if j := groups[1].Items[0]; j.Label != "lint" || j.Href != "?job=lint&ref=main&status=success" || j.Active { | |
| 168 | t.Errorf("lint: %+v", j) | |
| 169 | } | |
| 170 | if b := groups[2].Items[0]; b.Label != "main" || !b.Active || b.Href != "?status=success" { | |
| 171 | t.Errorf("active branch clears itself: %+v", b) | |
| 172 | } | |
| 173 | if b := groups[2].Items[1]; b.Label != "dev" || b.Active || b.Href != "?ref=dev&status=success" { | |
| 174 | t.Errorf("dev: %+v", b) | |
| 175 | } | |
| 176 | } | |
internal/httpd/repohead_test.go +2 −1
| @@ -25,10 +25,11 @@ func TestRepoHeaderTwoRows(t *testing.T) { | ||
| 25 | 25 | Runs []buildRun |
| 26 | 26 | Filter buildFilter |
| 27 | 27 | FilterLinks []buildFilterLink |
| 28 | Facets []facetGroup | |
| 28 | 29 | Refs []string |
| 29 | 30 | CanWrite bool |
| 30 | 31 | Notice string |
| 31 | }{p, nil, nil, nil, buildFilter{}, nil, nil, true, ""}) | |
| 32 | }{p, nil, nil, nil, buildFilter{}, nil, nil, nil, true, ""}) | |
| 32 | 33 | if err != nil { |
| 33 | 34 | t.Fatal(err) |
| 34 | 35 | } |
internal/web/templates/builds.html +14 −7
| @@ -1,19 +1,24 @@ | ||
| 1 | 1 | {{define "width"}}wide{{end}} |
| 2 | 2 | {{define "title"}}builds · {{.Repo.OwnerName}}/{{.Repo.Name}}{{end}} |
| 3 | 3 | {{define "content"}} |
| 4 | <div class="listhead"> | |
| 5 | <h1>Builds</h1> | |
| 6 | <nav class="filters"> | |
| 7 | {{range .FilterLinks}}<a {{if .Active}}class="active" aria-current="page" {{end}}href="{{.Href}}">{{.Label}}</a>{{end}} | |
| 8 | </nav> | |
| 4 | <div class="withcol"> | |
| 5 | <nav class="sidecol" aria-label="Filters"> | |
| 6 | {{range .Facets}}{{if .Items}}<div class="grp"> | |
| 7 | <h2 class="colhead">{{.Title}}</h2> | |
| 8 | <ul>{{range .Items}}<li><a{{if .Active}} aria-current="page"{{end}} href="{{.Href}}">{{.Label}}</a></li>{{end}}</ul> | |
| 9 | </div>{{end}}{{end}} | |
| 9 | 10 | <form method="get" class="searchform compact"> |
| 10 | <label for="ref">Branch</label> | |
| 11 | <label for="ref" class="colhead">Branch</label> | |
| 11 | 12 | <input type="text" id="ref" name="ref" value="{{.Filter.Ref}}" list="buildrefs"> |
| 12 | 13 | <datalist id="buildrefs">{{range .Refs}}<option value="{{.}}">{{end}}</datalist> |
| 13 | 14 | <input type="hidden" name="status" value="{{.Filter.Status}}"> |
| 14 | 15 | <input type="hidden" name="job" value="{{.Filter.Job}}"> |
| 15 | 16 | <button type="submit" class="btn">Filter</button> |
| 16 | 17 | </form> |
| 18 | </nav> | |
| 19 | <div class="colmain"> | |
| 20 | <div class="listhead"> | |
| 21 | <h1>Builds</h1> | |
| 17 | 22 | </div> |
| 18 | 23 | {{if .Notice}}<p class="error" role="alert">{{.Notice}}</p>{{end}} |
| 19 | 24 | {{if and .CanWrite .Jobs}} |
| @@ -31,7 +36,7 @@ | ||
| 31 | 36 | <p class="meta">Add <code>?job=name</code> for one job.</p> |
| 32 | 37 | </details> |
| 33 | 38 | <p class="meta">{{len .Runs}} run{{if ne (len .Runs) 1}}s{{end}}{{if or .Filter.Ref .Filter.Status .Filter.Job}}, <a href="?">clear filters</a>{{end}}</p> |
| 34 | <ul class="loglist"> | |
| 39 | <ul class="loglist rows"> | |
| 35 | 40 | {{range .Runs}}<li> |
| 36 | 41 | <div class="commitmain"> |
| 37 | 42 | <p class="subject"><code><a href="/{{$.Repo.OwnerName}}/{{$.Repo.Name}}/commit/{{.SHA}}">{{printf "%.10s" .SHA}}</a></code></p> |
| @@ -44,4 +49,6 @@ | ||
| 44 | 49 | </li> |
| 45 | 50 | {{else}}<li class="empty">no builds — push a commit with a <code>.gitbay/ci.yml</code></li>{{end}} |
| 46 | 51 | </ul> |
| 52 | </div> | |
| 53 | </div> | |
| 47 | 54 | {{end}} |