package markup
import (
"strings"
"testing"
)
// landingResolver knows landing 12 and nothing else.
type landingResolver struct{ fakeResolver }
func (landingResolver) ResolveToken(t RefToken) (string, bool) {
if t.Kind == RefLanding && t.ID == "12" && (t.Repo == "" || t.Repo == "demo") {
return "/r/q/landing/a1b2c3d4e5f6", true
}
return "", false
}
// !N names a landing by its number: a link with the preview hooks, in
// prose, repo-qualified or not.
func TestALandingNumberLinksWithAPreview(t *testing.T) {
for _, src := range []string{"landed in !12 yesterday", "see demo:!12.", "(!12)", "!12"} {
out, err := Render([]byte(src), landingResolver{})
if err != nil {
t.Fatal(err)
}
html := string(out)
if !strings.Contains(html, `href="/r/q/landing/a1b2c3d4e5f6"`) ||
!strings.Contains(html, `data-ref-kind="landing"`) ||
!strings.Contains(html, `data-ref="`) {
t.Errorf("FAIL: %q: no landing link with preview hooks: %s", src, html)
}
}
if tok, ok := ParseRef("!12"); !ok || tok.Kind != RefLanding || tok.ID != "12" {
t.Errorf("FAIL: ParseRef(!12) = %+v %v", tok, ok)
}
if tok, ok := ParseRef("demo:!12"); !ok || tok.Kind != RefLanding || tok.Repo != "demo" || tok.ID != "12" {
t.Errorf("FAIL: ParseRef(demo:!12) = %+v %v", tok, ok)
}
if !t.Failed() {
t.Log("ok: !12 and demo:!12 link to the landing with data-ref hooks")
}
}
// !N stays text inside code, inside a link, in image syntax, after a
// word or another bang, and where no such landing exists.
func TestALandingNumberStaysTextWhereItIsNotAReference(t *testing.T) {
for _, src := range []string{
"`!12`",
"```\n!12\n```",
"[about !12](https://example.com/x)",
"",
"",
"wow!12",
"!!12",
"!0 and !012",
"!13",
} {
out, err := Render([]byte(src), landingResolver{})
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(out), "/landing/a1b2c3d4e5f6") {
t.Errorf("FAIL: %q linked a landing: %s", src, out)
}
}
if !t.Failed() {
t.Log("ok: code, links, images, words, !! and unknown numbers stay text")
}
}
// Closing words close tickets, never a landing.
func TestClosingWordsNeverNameALanding(t *testing.T) {
for _, ref := range ClosingRefs("Closes !12. Fixes !3, resolves demo:!4") {
if ref.Kind == RefLanding {
t.Fatalf("FAIL: a closing word names landing %+v", ref)
}
t.Fatalf("FAIL: a closing word over !N names %+v", ref)
}
t.Log("ok: closes/fixes/resolves !N name nothing to close")
}
type describingLandings struct{ landingResolver }
func (describingLandings) DescribeLanding(t RefToken) (LandingRef, bool) {
if t.Kind == RefLanding && t.ID == "12" {
return LandingRef{Number: 12, State: "accepted"}, true
}
return LandingRef{}, false
}
// A resolved !N is the landing's state icon and its number, named for
// a reader who cannot see the glyph; an unresolved one stays as typed.
func TestAResolvedLandingWearsItsStateIconAndNumber(t *testing.T) {
out, _ := Render([]byte("landed in !12 and demo:!12"), describingLandings{})
html := string(out)
for _, want := range []string{`</svg>12</a>`, `</svg>demo:12</a>`, `data-ic="landing-accepted"`, `aria-label="landing 12, accepted"`, `href="#i-tkt-done"`, `data-ref-kind="landing"`} {
if !strings.Contains(html, want) {
t.Errorf("missing %s in %s", want, html)
}
}
if strings.Contains(html, ">!12<") || strings.Contains(html, "Open</span>") {
t.Errorf("a bang or a state badge remains: %s", html)
}
out, _ = Render([]byte("and !13"), describingLandings{})
if !strings.Contains(string(out), "!13") || strings.Contains(string(out), "<svg") {
t.Errorf("an unresolved !13 changed: %s", out)
}
}