penguin/flatgit

flatgit showcase

internal/render/markdown_test.go

raw ยท 5424 bytes

package render

import (
	"strings"
	"testing"
)

func TestMarkdownImageURL(t *testing.T) {
	tests := []struct {
		name, destination, markdownPath, rawBase, want string
	}{
		{"summary", "images/logo.png", "README.md", "raw/master", "raw/master/images/logo.png"},
		{"blob", "images/logo.png", "README.md", "../../raw/master", "../../raw/master/images/logo.png"},
		{"nested", "./images/logo.png", "docs/guide.md", "../../../raw/master", "../../../raw/master/docs/images/logo.png"},
		{"parent", "../images/logo.png", "docs/guide.md", "../../../raw/master", "../../../raw/master/images/logo.png"},
		{"root", "/images/logo.png", "docs/guide.md", "../../../raw/master", "../../../raw/master/images/logo.png"},
		{"commit", "logo.png", "docs/guide.md", "../../../raw/abc123", "../../../raw/abc123/docs/logo.png"},
		{"escaping", "images/my%20logo%23%3F.png?v=1#preview", "README.md", "raw/master", "raw/master/images/my%20logo%23%3F.png?v=1#preview"},
		{"traversal", "../../../logo.png", "docs/guide.md", "../../../raw/master", "../../../raw/master/logo.png"},
		{"https", "https://example.com/logo.png", "README.md", "raw/master", "https://example.com/logo.png"},
		{"protocol relative", "//example.com/logo.png", "README.md", "raw/master", "//example.com/logo.png"},
		{"data", "data:image/png;base64,AAAA", "README.md", "raw/master", "data:image/png;base64,AAAA"},
		{"fragment", "#logo", "README.md", "raw/master", "#logo"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := markdownImageURL(tt.destination, tt.markdownPath, tt.rawBase); got != tt.want {
				t.Fatalf("got %q, want %q", got, tt.want)
			}
		})
	}
}

func TestRenderMarkdownImages(t *testing.T) {
	src := []byte("![inline](../images/logo.png)\n\n![reference][logo]\n\n[logo]: ./local.png \"Title\"\n\n[link](../images/logo.png)\n\n![external](https://example.com/logo.png)\n\n![unsafe](javascript:alert(1))\n\n`![code](local.png)`\n")
	html, err := renderMarkdownSafe(src, "docs/guide.md", "../../../raw/master", "../../../blob/master")
	if err != nil {
		t.Fatal(err)
	}
	for _, want := range []string{
		`src="../../../raw/master/images/logo.png"`,
		`src="../../../raw/master/docs/local.png"`,
		`title="Title"`,
		`href="../images/logo.png"`,
		`src="https://example.com/logo.png"`,
		`<code>![code](local.png)</code>`,
	} {
		if !strings.Contains(string(html), want) {
			t.Errorf("missing %q in %s", want, html)
		}
	}
	if strings.Contains(string(html), "javascript:") {
		t.Errorf("unsafe image URL survived sanitization: %s", html)
	}
}

func TestMarkdownLinkURL(t *testing.T) {
	tests := []struct {
		name, destination, markdownPath, blobBase, want string
	}{
		{"summary", "docs/guide.md", "README.md", "blob/master", "blob/master/docs/guide.md.html"},
		{"blob", "guide.md", "README.md", "../../blob/master", "../../blob/master/guide.md.html"},
		{"nested", "./intro.md", "docs/guide.md", "../../../blob/master", "../../../blob/master/docs/intro.md.html"},
		{"parent", "../README.md#setup", "docs/guide.md", "../../../blob/master", "../../../blob/master/README.md.html#setup"},
		{"root", "/README.md", "docs/guide.md", "../../../blob/master", "../../../blob/master/README.md.html"},
		{"commit", "intro.md", "docs/guide.md", "../../../blob/abc123", "../../../blob/abc123/docs/intro.md.html"},
		{"escaping", "my%20guide%23%3F.md?v=1#setup", "README.md", "blob/master", "blob/master/my%20guide%23%3F.md.html?v=1#setup"},
		{"uppercase", "GUIDE.MD", "README.md", "blob/master", "blob/master/GUIDE.MD.html"},
		{"traversal", "../../../guide.md", "docs/guide.md", "../../../blob/master", "../../../blob/master/guide.md.html"},
		{"https", "https://example.com/guide.md", "README.md", "blob/master", "https://example.com/guide.md"},
		{"protocol relative", "//example.com/guide.md", "README.md", "blob/master", "//example.com/guide.md"},
		{"fragment", "#setup", "README.md", "blob/master", "#setup"},
		{"query only", "?file=guide.md", "README.md", "blob/master", "?file=guide.md"},
		{"non markdown", "image.png", "README.md", "blob/master", "image.png"},
		{"already rendered", "guide.md.html", "README.md", "blob/master", "guide.md.html"},
		{"invalid", "bad%ZZ.md", "README.md", "blob/master", "bad%ZZ.md"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := markdownLinkURL(tt.destination, tt.markdownPath, tt.blobBase); got != tt.want {
				t.Fatalf("got %q, want %q", got, tt.want)
			}
		})
	}
}

func TestRenderMarkdownLinks(t *testing.T) {
	src := []byte("[inline](../README.md#setup)\n\n[reference][guide]\n\n[guide]: ./intro.md?v=1#heading \"Guide\"\n\n![image](./diagram.md)\n\n[external](https://example.com/guide.md)\n\n[anchor](#heading)\n\n[unsafe](javascript:alert(1))\n\n`[code](intro.md)`\n")
	html, err := renderMarkdownSafe(src, "docs/guide.md", "../../../raw/master", "../../../blob/master")
	if err != nil {
		t.Fatal(err)
	}
	for _, want := range []string{
		`href="../../../blob/master/README.md.html#setup"`,
		`href="../../../blob/master/docs/intro.md.html?v=1#heading"`,
		`title="Guide"`,
		`src="../../../raw/master/docs/diagram.md"`,
		`href="https://example.com/guide.md"`,
		`href="#heading"`,
		`<code>[code](intro.md)</code>`,
	} {
		if !strings.Contains(string(html), want) {
			t.Errorf("missing %q in %s", want, html)
		}
	}
	if strings.Contains(string(html), "javascript:") {
		t.Errorf("unsafe link URL survived sanitization: %s", html)
	}
}