package clean import ( "reflect" "strings" "testing" "golang.org/x/net/html" "golang.org/x/net/html/atom" ) func TestParseSrcset(t *testing.T) { tests := []struct { in string want []candidate }{ {"a.jpg 400w, b.jpg 1500w", []candidate{{url: "a.jpg", width: 400}, {url: "b.jpg", width: 1500}}}, {"a.jpg, b.jpg 2x", []candidate{{url: "a.jpg"}, {url: "b.jpg", density: 2}}}, {" a.jpg 1x ,\n b.jpg 2x ", []candidate{{url: "a.jpg", density: 1}, {url: "b.jpg", density: 2}}}, // Commas inside URLs, as some image CDNs use. {"https://cdn.example/w_400,h_300/a.jpg 400w, https://cdn.example/w_1600,h_1200/a.jpg 1600w", []candidate{{url: "https://cdn.example/w_400,h_300/a.jpg", width: 400}, {url: "https://cdn.example/w_1600,h_1200/a.jpg", width: 1600}}}, {"single.jpg", []candidate{{url: "single.jpg"}}}, {"data:image/gif;base64,R0lG 1x, real.jpg 2x", []candidate{{url: "real.jpg", density: 2}}}, {"", nil}, } for _, tt := range tests { if got := parseSrcset(tt.in); !reflect.DeepEqual(got, tt.want) { t.Errorf("parseSrcset(%q) = %+v, want %+v", tt.in, got, tt.want) } } } func TestPickCandidate(t *testing.T) { tests := []struct { srcset, want string }{ {"a 400w, b 1500w, c 3000w", "b"}, // smallest that is at least 1404 wide {"c 3000w, b 1500w, a 400w", "b"}, // order doesn't matter {"a 400w, b 800w", "b"}, // none wide enough: the widest {"a 1x, b 2x, c 1.5x", "b"}, // densities: the highest {"a, b", "a"}, // no descriptors: the first {"", ""}, } for _, tt := range tests { if got := pickCandidate(parseSrcset(tt.srcset)); got != tt.want { t.Errorf("pickCandidate(%q) = %q, want %q", tt.srcset, got, tt.want) } } } func TestPrepareImages(t *testing.T) { tests := []struct{ name, in, want string }{ {"relative src made absolute", `p`, `p`}, {"lazy data-src replaces placeholder", `p`, `p`}, {"data-original", ``, ``}, {"srcset picks the right width", ``, ``}, {"picture skips AVIF sources and uses WebP", ``, ``}, {"picture with only AVIF falls back to the img", ``, ``}, {"tracking pixels are removed", `

ab

`, `

ab

`}, {"captions laid out with divs are reduced to text", `
`, `

The new dialog. Credit: KDE

`}, {"zoom links around images are unwrapped", `
x
`, `
x
`}, {"links with text around images are kept", `

Read more

`, `

Read more

`}, {"figcaption becomes an italic paragraph", `
A bold caption
`, `

A bold caption

`}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := prepareString(t, tt.in); got != tt.want { t.Errorf("got %q\nwant %q", got, tt.want) } }) } } func TestBuildFigures(t *testing.T) { tests := []struct{ name, in, want string }{ {"standalone image with caption", `A

The caption

Text.

`, `
A
The caption

Text.

`}, {"em caption", `

Caption

`, `
Caption
`}, {"standalone image without caption", `

Just a paragraph with some italics.

`, `

Just a paragraph with some italics.

`}, {"image inside a paragraph stays inline", `

Look here.

`, `

Look here.

`}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := cleanString(t, tt.in); got != tt.want { t.Errorf("got %q\nwant %q", got, tt.want) } }) } } func TestStandInSources(t *testing.T) { for _, src := range []string{ "https://thumb.example.org/a/Photo.jpg/500px-Photo.jpg?utm_source=en.wikipedia.org&utm_campaign=parser", "https://images.example.org/media/12345", "data:image/png;base64,iVBORw0KGgo=", "https://example.com/plain.png", } { standIn := protectSrc(src) if !strings.HasPrefix(standIn, "https://rekopub.invalid/img/") || !strings.HasSuffix(standIn, ".png") { t.Errorf("protectSrc(%q) = %q, want a rekopub.invalid URL ending in .png", src, standIn) } if got := restoreSrc(standIn); got != src { t.Errorf("restoreSrc(protectSrc(%q)) = %q", src, got) } } if got := restoreSrc("https://example.com/a.png"); got != "https://example.com/a.png" { t.Errorf("restoreSrc changed an ordinary URL: %q", got) } if got := restoreSrc("https://rekopub.invalid/img/!!!.png"); got != "" { t.Errorf("restoreSrc of a damaged stand-in = %q, want empty", got) } } func TestPrepareUsesStandIns(t *testing.T) { doc, err := html.Parse(strings.NewReader(``)) if err != nil { t.Fatal(err) } Prepare(doc, base) var src string walk(doc, func(n *html.Node) { if n.DataAtom == atom.Img { src = attr(n, "src") } }) if !strings.HasPrefix(src, srcStandIn) || restoreSrc(src) != "https://example.com/a.jpg?x=1" { t.Errorf("src after Prepare = %q", src) } }