Skip to content
quarry Artifact [4b8904a588]
Login

Artifact [4b8904a588]

Artifact 4b8904a5884b4e4495633b10a03ad212990372252d5554750a28b8086cd206f3:


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)",
		"![12](https://example.com/i.png)",
		"![!12](https://example.com/i.png)",
		"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)
	}
}