Skip to content
quarry Artifact [7883d6e165]
Login

Artifact [7883d6e165]

Artifact 7883d6e165fe6c71ae44c0dbf8ddf568c2453eccf7712219502b20acc3329049:


package markup

import (
	"strings"
	"testing"
)

// hostResolver is a resolver that knows which repository its pages are
// served from — the upgrade that turns a reference link into a
// previewable one (RFC 0011 §14).
type hostResolver struct{ fakeResolver }

func (hostResolver) RefRepo() string { return "de mo" }

func (hostResolver) ResolveToken(t RefToken) (string, bool) {
	if t.Kind == RefTicket {
		return "/r/demo/ticket/" + t.ID, true
	}
	return "", false
}

// Every reference on every surface carries the same hooks — the
// unqualified [[wiki]] and bare-hash spellings included. A reader
// hovering a hash in a ticket and a repo-qualified token in chat is
// doing the same thing and must get the same card.
func TestEveryReferenceCarriesThePreviewHook(t *testing.T) {
	src := "See [[Design]], abc123def456 and /r/demo/ticket/xy9deadbeef too\n"
	out, err := Render([]byte(src), hostResolver{})
	if err != nil {
		t.Fatal(err)
	}
	s := string(out)
	for _, want := range []string{
		`data-ref="wiki/Design"`,
		`data-ref-kind="wiki"`,
		`data-ref="[abc123def456]"`,
		`data-ref-kind="hash"`,
		`data-ref="demo:ticket/xy9deadbeef"`,
	} {
		if !strings.Contains(s, want) {
			t.Fatalf("missing hook %s in:\n%s", want, s)
		}
	}
	// One armed anchor per reference: hover, focus and tap.
	for _, ev := range []string{"data-on:mouseenter", "data-on:focus", "data-on:click"} {
		if n := strings.Count(s, ev); n != 3 {
			t.Fatalf("%s appears %d times, want 3 in:\n%s", ev, n, s)
		}
	}
	// The repo rides the fragment URL escaped, never raw.
	if !strings.Contains(s, "/r/de+mo/ref.frag?ref=") {
		t.Fatalf("fragment URL not escaped:\n%s", s)
	}
	// The hash link keeps the styling it has had since 0002: the hook
	// is added, the look is not changed.
	if strings.Contains(s, `class="ref"`) && !strings.Contains(s, `class="ref" data-ref="demo:ticket/xy9deadbeef"`) {
		t.Fatalf("hash link restyled:\n%s", s)
	}
}

// The card is positioned AT the link that opened it, not centred by
// the UA. CSS anchor positioning where it exists, a measured fallback
// where it does not.
func TestPreviewAttrsPositionTheCardAtTheLink(t *testing.T) {
	var joined string
	for _, kv := range RefPreviewAttrs("demo", "#5") {
		joined += kv[0] + "=" + kv[1] + "\n"
	}
	for _, want := range []string{
		"position-anchor", "anchorName", "getBoundingClientRect",
		"showPopover", "refpop",
	} {
		if !strings.Contains(joined, want) {
			t.Fatalf("preview attrs miss %q:\n%s", want, joined)
		}
	}
}

// A resolver that does not know its repository mints the link and no
// hooks: there is no fragment URL to fetch, and a hook nothing arms is
// a hook that lies.
func TestNoRepoMeansNoHooks(t *testing.T) {
	out, err := Render([]byte("See [[Design]].\n"), fakeResolver{})
	if err != nil {
		t.Fatal(err)
	}
	if strings.Contains(string(out), "data-on:") {
		t.Fatalf("hooks minted without a repository:\n%s", out)
	}
}