package web
import (
"strings"
"testing"
)
// The check-in page offers to propose a branch only where the offer is
// true: at the tip, on a branch that is not the target, with nothing
// already proposing it. Anywhere else the link would name work the
// reader is not looking at, or duplicate a landing that exists.
func TestProposeLinkAppearsOnlyWhenItIsTrue(t *testing.T) {
render := func(d pageData) string {
t.Helper()
var sb strings.Builder
d.Repo, d.Checkin.Hash = "alpha", strings.Repeat("a", 64)
d.Checkin.Branch = "feature"
if err := checkinPage.Execute(&sb, d); err != nil {
t.Fatal(err)
}
return sb.String()
}
offered := render(pageData{ProposeBranch: "feature", ProposeTarget: "trunk"})
if !strings.Contains(offered, `href="/r/alpha/landing-new?branch=feature"`) {
t.Error("a branch tip with nothing proposing it offers no way to propose it")
}
// The form is linked, not submitted: the title and the reasoning are
// what everyone reads afterwards.
if strings.Contains(offered, `action="/r/alpha/landing-new"`) {
t.Error("the page opens a landing directly instead of linking the form")
}
// Nothing to propose: no link at all, not a disabled one.
if got := render(pageData{}); strings.Contains(got, "landing-new") {
t.Error("a check-in with nothing to propose still offers it")
}
// Already proposed: the landing is named instead of a second button.
taken := render(pageData{OpenLanding: &landingRow{
ID: strings.Repeat("b", 64), Branch: "feature", Target: "trunk", Title: "the work"}})
if strings.Contains(taken, "landing-new") {
t.Error("a branch that is already proposed is offered a second landing")
}
if !strings.Contains(taken, `href="/r/alpha/landing/bbbbbbbbbbbb"`) {
t.Errorf("the open landing is not linked:\n%s", taken)
}
if !strings.Contains(taken, "the work") {
t.Error("the open landing is named without saying what it proposes")
}
}
// The offer is built from four facts, and each one on its own can make
// it a lie. This pins the reasons rather than the wording: a closed
// branch is the author saying it is done, and offering to propose one
// is how a branch that had already landed was offered a second, empty
// landing.
func TestProposeAsksTheQuestionsThatMakeTheOfferTrue(t *testing.T) {
tip := strings.Repeat("a", 64)
cases := []struct {
name string
branch, deft string
branchTip string
open bool
landing *landingRow
want bool
}{
{"the tip of an open unproposed branch", "feature", "trunk", tip, true, nil, true},
{"not the tip", "feature", "trunk", strings.Repeat("b", 64), true, nil, false},
{"the target itself", "trunk", "trunk", tip, true, nil, false},
{"a closed branch", "feature", "trunk", tip, false, nil, false},
{"already proposed", "feature", "trunk", tip, true, &landingRow{Branch: "feature"}, false},
{"no branch at all", "", "trunk", tip, true, nil, false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := proposable(tc.branch, tip, tc.branchTip, tc.deft, tc.open, tc.landing)
if got != tc.want {
t.Errorf("offered=%v, want %v", got, tc.want)
}
})
}
}