package web
import (
"bytes"
"strings"
"testing"
)
// The merge page says each verdict in its own words, draws the view for
// a conflict, and links the pair's open landing.
func TestMergePageSaysEachVerdict(t *testing.T) {
render := func(mp *mergePage) string {
var b bytes.Buffer
if err := noHooks.template(mergePageTmpl).ExecuteTemplate(&b, "content", pageData{Repo: "alpha", Merge: mp}); err != nil {
t.Fatal(err)
}
return b.String()
}
for verdict, hook := range map[string]string{"clean": "merge-clean", "up-to-date": "merge-uptodate", "unrelated": "merge-unrelated"} {
h := render(&mergePage{Branch: "feat", Onto: "trunk", Verdict: verdict, Branches: []string{"trunk", "feat"}})
if !strings.Contains(h, `data-hook="`+hook+`"`) || strings.Contains(h, `data-hook="meld"`) {
t.Errorf("FAIL: %s did not say so on its own", verdict)
}
}
mv := buildMeld(meldFixture(), "/r/alpha")
h := render(&mergePage{Branch: "feat", Onto: "trunk", Verdict: "conflict", Meld: mv, Landing: "7", LandingNumber: "7"})
if !strings.Contains(h, `data-hook="meld"`) || !strings.Contains(h, `data-verdict="conflict"`) {
t.Error("FAIL: a conflicting pair does not draw the merge view")
}
if !strings.Contains(h, `data-hook="merge-landing"`) || !strings.Contains(h, `/r/alpha/landing/7`) {
t.Error("FAIL: the pair's open landing is not linked")
}
if !strings.Contains(h, `name="branch"`) || !strings.Contains(h, `name="onto"`) {
t.Error("FAIL: no way to pick another pair")
}
}
// The branches list gives each open branch its standing against trunk,
// linking the branch's own merge page; the default branch has none.
func TestBranchesListShowsHowEachBranchMerges(t *testing.T) {
rows := []branchRow{{Name: "trunk", Tip: "t1"}, {Name: "feat", Tip: "f1"}, {Name: "docs", Tip: "d1"}, {Name: "new", Tip: "n1"}}
type m = branchMerge
rows[1].Merge = &m{Onto: "trunk", State: "conflict", Files: 2}
rows[2].Merge = &m{Onto: "trunk", State: "clean", Behind: 3}
rows[3].Merge = &m{Onto: "trunk"}
var b bytes.Buffer
if err := noHooks.template(branchesPage).ExecuteTemplate(&b, "content", pageData{Repo: "alpha", Branches: rows}); err != nil {
t.Fatal(err)
}
h := b.String()
for _, want := range []string{`data-state="conflict"`, `data-state="clean"`, `data-state=""`,
`/r/alpha/merge?branch=feat&onto=trunk`, `/r/alpha/merge?branch=docs&onto=trunk`} {
if !strings.Contains(h, want) {
t.Errorf("FAIL: the list lacks %s", want)
}
}
if strings.Count(h, `data-hook="branch-merge"`) != 3 {
t.Error("FAIL: the default branch, or a branch, has the wrong number of merge chips")
}
}
// The page offers what the reader's push would be allowed: a choice of
// side to commit onto, a refusal that says who owns the target, and a
// landing for the pair; a check-in's merge goes onto the target only.
func TestMergePageOffersWhatAPushWouldAllow(t *testing.T) {
render := func(mp *mergePage) string {
var b bytes.Buffer
if err := noHooks.template(mergePageTmpl).ExecuteTemplate(&b, "content", pageData{Repo: "alpha", Merge: mp}); err != nil {
t.Fatal(err)
}
return b.String()
}
mv := buildMeld(meldFixture(), "/r/alpha")
mv.CanResolve = true
mv.Into = []meldInto{{Value: "onto", Name: "trunk", Checked: true}, {Value: "branch", Name: "feat"}}
h := render(&mergePage{Branch: "feat", Onto: "trunk", Verdict: "conflict", Meld: mv, MayMerge: true, MayPropose: true})
if strings.Count(h, `name="into"`) != 2 || !strings.Contains(h, `data-hook="merge-into"`) {
t.Error("FAIL: a reader who may push to both sides is not offered the choice")
}
if !strings.Contains(h, `data-hook="merge-propose"`) || !strings.Contains(h, `landing-new?branch=feat&target=trunk`) {
t.Error("FAIL: no landing is offered for the pair")
}
if strings.Contains(h, `data-hook="merge-refused"`) {
t.Error("FAIL: a reader who may push is told they are refused")
}
h = render(&mergePage{Branch: "feat", Onto: "trunk", Verdict: "clean", Refused: true, Owners: "alice", MayPropose: true})
if !strings.Contains(h, `data-hook="merge-refused"`) || !strings.Contains(h, "alice") {
t.Error("FAIL: a protected target does not say so, or by whom")
}
if !strings.Contains(h, `class="btn btn-primary" data-hook="merge-propose"`) {
t.Error("FAIL: refused, the landing is not the page's one action")
}
h = render(&mergePage{Branch: "feat", Onto: "trunk", Verdict: "clean", MayPropose: true, Landing: "3", LandingNumber: "3"})
if strings.Contains(h, `data-hook="merge-propose"`) {
t.Error("FAIL: a second landing is offered for a pair that has one")
}
one := buildMeld(meldFixture(), "/r/alpha")
one.CanResolve = true
one.Into = []meldInto{{Value: "onto", Name: "trunk", Checked: true}}
h = render(&mergePage{Branch: "0123456789ab", Onto: "trunk", Verdict: "conflict", Meld: one, Checkin: true, MayMerge: true})
if strings.Count(h, `name="into"`) != 1 || strings.Contains(h, `type="radio" name="into"`) {
t.Error("FAIL: a check-in's merge offers a side other than the target")
}
if strings.Contains(h, `data-hook="merge-propose"`) {
t.Error("FAIL: a landing is offered for a check-in")
}
if !strings.Contains(h, `<option selected>0123456789ab</option>`) {
t.Error("FAIL: the picker does not show the check-in being merged")
}
for _, v := range []string{"up-to-date", "unrelated"} {
h = render(&mergePage{Branch: "feat", Onto: "trunk", Verdict: v, Refused: true, MayPropose: true})
if strings.Contains(h, `data-hook="merge-refused"`) || strings.Contains(h, `data-hook="merge-propose"`) {
t.Errorf("FAIL: %s offers a merge that has nothing to do", v)
}
}
}
// A branch's own timeline says how it merges onto the default branch,
// linking its merge page, where a reader thinking "merge this" stands.
func TestBranchTimelineLinksItsMerge(t *testing.T) {
var b bytes.Buffer
data := pageData{Repo: "alpha", Branch: "feat", BranchMerge: &branchMerge{Onto: "trunk", State: "conflict", Files: 1}}
if err := noHooks.template(timelinePage).ExecuteTemplate(&b, "content", data); err != nil {
t.Fatal(err)
}
h := b.String()
if !strings.Contains(h, `data-hook="branch-merge-line"`) || !strings.Contains(h, `/r/alpha/merge?branch=feat&onto=trunk`) {
t.Error("FAIL: the branch's timeline does not link its merge")
}
b.Reset()
if err := noHooks.template(timelinePage).ExecuteTemplate(&b, "content", pageData{Repo: "alpha"}); err != nil {
t.Fatal(err)
}
if strings.Contains(b.String(), `data-hook="branch-merge-line"`) {
t.Error("FAIL: the whole timeline claims a branch's merge")
}
}