master
  1package links
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"github.com/antonmedv/gitmal/pkg/git"
  8)
  9
 10func buildTestSets(blobs []git.Blob) (Set, Set) {
 11	dirs := BuildDirSet(blobs)
 12	files := BuildFileSet(blobs)
 13	return dirs, files
 14}
 15
 16func TestResolve_Links(t *testing.T) {
 17	blobs := []git.Blob{
 18		{Path: "README.md"},
 19		{Path: "docs/intro.md"},
 20		{Path: "docs/getting-started.md"},
 21		{Path: "docs/faq.md"},
 22		{Path: "docs/tutorial/step1.md"},
 23	}
 24
 25	dirs, files := buildTestSets(blobs)
 26
 27	currentPath := "docs/intro.md"
 28	rootHref := "../../"
 29	ref := "master"
 30
 31	tests := []struct {
 32		name        string
 33		content     string
 34		wantContain []string
 35		notContain  []string
 36	}{
 37		{
 38			name:    "relative link to existing .md file gets .html appended (per current transformHref)",
 39			content: `<a href="getting-started.md">Getting started</a>`,
 40			wantContain: []string{
 41				`href="../../blob/master/docs/getting-started.md.html"`,
 42			},
 43			notContain: []string{
 44				`href="../../blob/master/docs/getting-started.md"`,
 45			},
 46		},
 47		{
 48			name:    "relative link without extension to existing .md file gets .html appended",
 49			content: `<a href="faq">FAQ</a>`,
 50			wantContain: []string{
 51				`href="../../blob/master/docs/faq.md.html"`,
 52			},
 53			notContain: []string{
 54				`href="faq">`,
 55			},
 56		},
 57		{
 58			name:    "relative link to directory without trailing slash goes to /index.html",
 59			content: `<a href="tutorial">Tutorial</a>`,
 60			wantContain: []string{
 61				`href="../../blob/master/docs/tutorial/index.html"`,
 62			},
 63			notContain: []string{
 64				`href="tutorial">`,
 65			},
 66		},
 67		{
 68			name:    "relative link to directory with trailing slash goes to /index.html",
 69			content: `<a href="tutorial/">Tutorial</a>`,
 70			wantContain: []string{
 71				`href="../../blob/master/docs/tutorial/index.html"`,
 72			},
 73			notContain: []string{
 74				`href="tutorial/">`,
 75			},
 76		},
 77		{
 78			name:    "absolute http URL unchanged",
 79			content: `<a href="https://example.org">External</a>`,
 80			wantContain: []string{
 81				`href="https://example.org"`,
 82			},
 83		},
 84		{
 85			name:    "root-relative link to repo file is resolved",
 86			content: `<a href="/README.md">FAQ</a>`,
 87			wantContain: []string{
 88				`href="../../blob/master/README.md.html"`,
 89			},
 90		},
 91		{
 92			name:    "fragment-only href unchanged",
 93			content: `<a href="#section1">Jump</a>`,
 94			wantContain: []string{
 95				`href="#section1"`,
 96			},
 97		},
 98		{
 99			name:    "mailto href unchanged",
100			content: `<a href="mailto:test@example.com">Mail</a>`,
101			wantContain: []string{
102				`href="mailto:test@example.com"`,
103			},
104		},
105		{
106			name:    "unknown relative path left untouched",
107			content: `<a href="unknown.md">Unknown</a>`,
108			wantContain: []string{
109				`href="unknown.md"`,
110			},
111		},
112	}
113
114	for _, tt := range tests {
115		t.Run(tt.name, func(t *testing.T) {
116			got := Resolve(tt.content, currentPath, rootHref, ref, dirs, files)
117
118			for _, want := range tt.wantContain {
119				if !strings.Contains(got, want) {
120					t.Errorf("expected output to contain %q, got:\n%s", want, got)
121				}
122			}
123			for _, notWant := range tt.notContain {
124				if strings.Contains(got, notWant) {
125					t.Errorf("expected output NOT to contain %q, got:\n%s", notWant, got)
126				}
127			}
128		})
129	}
130}
131
132func TestResolve_Images(t *testing.T) {
133	// For image behavior we only care about currentPath/rootHref/ref, not dirs/files.
134	blobs := []git.Blob{
135		{Path: "foo/bar/readme.md"},
136	}
137	dirs, files := buildTestSets(blobs)
138
139	rootHref := "../../"
140	ref := "master"
141
142	t.Run("relative image src rewritten to raw URL with ref", func(t *testing.T) {
143		// current blob: foo/bar/readme.md
144		// img src:      ../images/pic.png
145		// repoPath:     foo/images/pic.png
146		// final:        rootHref + "/raw/master/foo/images/pic.png"
147		content := `<p><img src="../images/pic.png" alt="Pic"></p>`
148
149		got := Resolve(content, "foo/bar/readme.md", rootHref, ref, dirs, files)
150
151		expected := `src="../../raw/master/foo/images/pic.png"`
152		if !strings.Contains(got, expected) {
153			t.Fatalf("expected output to contain %q, got:\n%s", expected, got)
154		}
155	})
156
157	t.Run("relative image src with ./ prefix", func(t *testing.T) {
158		content := `<p><img src="./img/logo.png"></p>`
159
160		got := Resolve(content, "foo/readme.md", rootHref, ref, dirs, files)
161
162		// repoPath: "foo/img/logo.png"
163		expected := `src="../../raw/master/foo/img/logo.png"`
164		if !strings.Contains(got, expected) {
165			t.Fatalf("expected output to contain %q, got:\n%s", expected, got)
166		}
167	})
168
169	t.Run("absolute image src unchanged, root-relative resolved from repo root", func(t *testing.T) {
170		content := `
171<p>
172  <img src="https://cdn.example.com/img.png">
173  <img src="/static/logo.png">
174</p>`
175
176		got := Resolve(content, "docs/intro.md", rootHref, ref, dirs, files)
177
178		if !strings.Contains(got, `src="https://cdn.example.com/img.png"`) {
179			t.Errorf("expected absolute src to be unchanged, got:\n%s", got)
180		}
181		// root-relative should now point to raw/ref/... from repo root
182		if !strings.Contains(got, `src="../../raw/master/static/logo.png"`) {
183			t.Errorf("expected root-relative src to be resolved to raw URL, got:\n%s", got)
184		}
185	})
186
187	t.Run("image with query and fragment preserves them", func(t *testing.T) {
188		content := `<img src="../images/pic.png?size=large#anchor">`
189
190		got := Resolve(content, "foo/bar/readme.md", rootHref, ref, dirs, files)
191
192		if !strings.Contains(got, `src="../../raw/master/foo/images/pic.png?size=large#anchor"`) {
193			t.Errorf("expected src to be rewritten and keep query+fragment, got:\n%s", got)
194		}
195	})
196}
197
198func TestBuildDirSet(t *testing.T) {
199	blobs := []git.Blob{
200		{Path: "a/b/c.md"},
201		{Path: "a/d/e.md"},
202		{Path: "x.md"},
203	}
204
205	dirs := BuildDirSet(blobs)
206
207	wantDirs := []string{"a", "a/b", "a/d"}
208	for _, d := range wantDirs {
209		if _, ok := dirs[d]; !ok {
210			t.Errorf("expected dirs to contain %q", d)
211		}
212	}
213}
214
215func TestBuildFileSet(t *testing.T) {
216	blobs := []git.Blob{
217		{Path: "docs/intro.md"},
218		{Path: "README.md"},
219	}
220
221	files := BuildFileSet(blobs)
222
223	for _, p := range []string{"docs/intro.md", "README.md"} {
224		if _, ok := files[p]; !ok {
225			t.Errorf("expected files to contain %q", p)
226		}
227	}
228}