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}