From f3ac4dacf6e4ef8edc18649be7046fc0ce29972f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Guillot?= Date: Tue, 29 Jul 2025 21:46:54 -0700 Subject: [PATCH] test(rewrite): add unit tests for `addYoutubeVideoFromId` and `addInvidiousVideo` functions --- .../rewrite/content_rewrite_functions.go | 21 ++- .../reader/rewrite/content_rewrite_test.go | 132 ++++++++++++++++++ 2 files changed, 142 insertions(+), 11 deletions(-) diff --git a/internal/reader/rewrite/content_rewrite_functions.go b/internal/reader/rewrite/content_rewrite_functions.go index 3290d60b..35db76c3 100644 --- a/internal/reader/rewrite/content_rewrite_functions.go +++ b/internal/reader/rewrite/content_rewrite_functions.go @@ -273,9 +273,12 @@ func getYoutubVideoIDFromURL(entryURL string) string { return "" } +func buildVideoPlayerIframe(absoluteVideoURL string) string { + return `` +} + func addVideoPlayerIframe(absoluteVideoURL, entryContent string) string { - video := `` - return video + `
` + entryContent + return buildVideoPlayerIframe(absoluteVideoURL) + `
` + entryContent } func addYoutubeVideoRewriteRule(entryURL, entryContent string) string { @@ -292,29 +295,25 @@ func addYoutubeVideoUsingInvidiousPlayer(entryURL, entryContent string) string { return entryContent } +// For reference: https://github.com/miniflux/v2/pull/1314 func addYoutubeVideoFromId(entryContent string) string { matches := youtubeIdRegex.FindAllStringSubmatch(entryContent, -1) if matches == nil { return entryContent } - sb := strings.Builder{} + videoPlayerHTML := "" for _, match := range matches { if len(match) == 2 { - sb.WriteString(`
`) + videoPlayerHTML += buildVideoPlayerIframe(config.Opts.YouTubeEmbedUrlOverride()+match[1]) + "
" } } - sb.WriteString(entryContent) - return sb.String() + return videoPlayerHTML + entryContent } func addInvidiousVideo(entryURL, entryContent string) string { matches := invidioRegex.FindStringSubmatch(entryURL) if len(matches) == 3 { - video := `` - return video + `
` + entryContent + return addVideoPlayerIframe(`https://`+matches[1]+`/embed/`+matches[2], entryContent) } return entryContent } diff --git a/internal/reader/rewrite/content_rewrite_test.go b/internal/reader/rewrite/content_rewrite_test.go index b0b7564d..4ee0817d 100644 --- a/internal/reader/rewrite/content_rewrite_test.go +++ b/internal/reader/rewrite/content_rewrite_test.go @@ -195,6 +195,138 @@ func TestRewriteYoutubeShortLinkUsingInvidious(t *testing.T) { } } +func TestAddYoutubeVideoFromId(t *testing.T) { + config.Opts = config.NewOptions() + + scenarios := map[string]string{ + // Test with single YouTube ID + `Some content with youtube ID `: `
Some content with youtube ID `, + + // Test with multiple YouTube IDs + `Content with youtube_id: "dQw4w9WgXcQ" and youtube_id: "jNQXAC9IVRw"`: `

Content with youtube_id: "dQw4w9WgXcQ" and youtube_id: "jNQXAC9IVRw"`, + + // Test with YouTube ID using equals sign + `Some content with youtube_id = "dQw4w9WgXcQ"`: `
Some content with youtube_id = "dQw4w9WgXcQ"`, + + // Test with spaces around delimiters + `Some content with youtube_id : "dQw4w9WgXcQ"`: `
Some content with youtube_id : "dQw4w9WgXcQ"`, + + // Test with YouTube ID without quotes (regex requires quotes) + `Some content with youtube_id: dQw4w9WgXcQ and more`: `Some content with youtube_id: dQw4w9WgXcQ and more`, + + // Test with no YouTube ID + `Some regular content without any video ID`: `Some regular content without any video ID`, + + // Test with invalid YouTube ID (wrong length) + `Some content with youtube_id: "invalid"`: `Some content with youtube_id: "invalid"`, + + // Test with empty content + ``: ``, + } + + for input, expected := range scenarios { + actual := addYoutubeVideoFromId(input) + if actual != expected { + t.Errorf(`addYoutubeVideoFromId test failed for input "%s"`, input) + t.Errorf(`Expected: "%s"`, expected) + t.Errorf(`Actual: "%s"`, actual) + } + } +} + +func TestAddYoutubeVideoFromIdWithCustomEmbedURL(t *testing.T) { + os.Clearenv() + os.Setenv("YOUTUBE_EMBED_URL_OVERRIDE", "https://invidious.custom/embed/") + + var err error + parser := config.NewParser() + config.Opts, err = parser.ParseEnvironmentVariables() + + if err != nil { + t.Fatalf(`Parsing failure: %v`, err) + } + + input := `Some content with youtube_id: "dQw4w9WgXcQ"` + expected := `
Some content with youtube_id: "dQw4w9WgXcQ"` + + actual := addYoutubeVideoFromId(input) + if actual != expected { + t.Errorf(`addYoutubeVideoFromId with custom embed URL failed`) + t.Errorf(`Expected: "%s"`, expected) + t.Errorf(`Actual: "%s"`, actual) + } +} + +func TestAddInvidiousVideo(t *testing.T) { + scenarios := map[string][]string{ + // Test with various Invidious instances + "https://invidious.io/watch?v=dQw4w9WgXcQ": { + "Some video content", + `
Some video content`, + }, + "https://yewtu.be/watch?v=jNQXAC9IVRw": { + "Another video description", + `
Another video description`, + }, + "http://invidious.snopyta.org/watch?v=dQw4w9WgXcQ": { + "HTTP instance test", + `
HTTP instance test`, + }, + "https://youtube.com/watch?v=dQw4w9WgXcQ": { + "YouTube URL (also matches regex)", + `
YouTube URL (also matches regex)`, + }, + "https://example.org/watch?v=dQw4w9WgXcQ": { + "Any domain with watch pattern", + `
Any domain with watch pattern`, + }, + + // Test with query parameters + "https://invidious.io/watch?v=dQw4w9WgXcQ&t=30s": { + "Video with timestamp", + `
Video with timestamp`, + }, + + // Test with more complex query parameters + "https://invidious.io/watch?v=dQw4w9WgXcQ&t=30s&autoplay=1": { + "Video with multiple parameters", + `
Video with multiple parameters`, + }, + + // Test with non-matching URLs (should return content unchanged) + "https://invidious.io/": { + "Invidious homepage", + "Invidious homepage", + }, + "https://invidious.io/some-other-page": { + "Other page", + "Other page", + }, + "https://invidious.io/search?q=test": { + "Search page", + "Search page", + }, + + // Test with empty content + "https://empty.invidious.io/watch?v=dQw4w9WgXcQ": { + "", + `
`, + }, + } + + for entryURL, testData := range scenarios { + entryContent := testData[0] + expected := testData[1] + + actual := addInvidiousVideo(entryURL, entryContent) + if actual != expected { + t.Errorf(`addInvidiousVideo test failed for URL "%s" and content "%s"`, entryURL, entryContent) + t.Errorf(`Expected: "%s"`, expected) + t.Errorf(`Actual: "%s"`, actual) + } + } +} + func TestRewriteWithInexistingCustomRule(t *testing.T) { controlEntry := &model.Entry{ URL: "https://www.youtube.com/watch?v=1234",