summaryrefslogtreecommitdiff
path: root/app/src/test/java/org/terst/feedreader/readability/EvalOutcomeTest.kt
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-10-21 19:07:17 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-10-21 19:07:17 +0000
commit9210bf463a8818595f7ca42486a787d4f1941d55 (patch)
tree3a38cf5d71a20393a33eef5a19b1a97554055e75 /app/src/test/java/org/terst/feedreader/readability/EvalOutcomeTest.kt
parente50f4fbbca21b85648f6c4045cbd50f672d315cd (diff)
Fix stray promo headers, site-URL subscribing, and YouTube playback
Three reported bugs. **"West Hawaii today articles get a bunch of stray Trending headers."** The publisher's widget platform injects promo blocks between the real paragraphs, inside the article's own container, so every structural signal tells Readability they belong. What survives is a heading labelling nothing, because the links under it are filled in by a script that never runs in the reader. Confirmed on 9 of the 12 articles in that feed, two each. Blocks marked `data-mrf-recirculation` are now removed -- the attribute says what the block is for in the widget platform's own words, which is a better signal than the word "Trending", since an article may legitimately discuss what is trending. Nothing longer than a label and a few headlines is ever removed. Also applied to already-cached articles, in place rather than by discarding them: the article was fine, only the litter has to go. **"Can't subscribe to heathercoxrichardson.substack.com."** Adding a feed handed whatever URL it was given straight to the XML parser, so a site's home page -- the only URL a reader can actually see and copy -- failed on the root element. That page advertises its feed the way sites have since 2002, in a <link rel="alternate"> in the head. It is now read, resolved against the page, and fetched. Only what the page declares: guessing /feed and /rss would add a burst of 404s to every failed add and eventually subscribe somebody to the wrong thing. **"YouTube play is misconfigured."** It was, and YouTube said so: the player dialog loaded the embed as its own top-level document, which answers with "Video player configuration error -- Error 153". An embed has to be embedded. It now sits in an iframe on a minimal host page. Measured in the app's own WebView: top-level, no requests for the video stream and the error on screen; framed, the stream loads and there is no error. **"Long click on YouTube placeholder should open the URL directly."** It does now. Worth recording why this is not simply the link's href: for an image inside a link, WebView's hit test reports the image's URL, so a long-press on the thumbnail arrives as the i.ytimg.com JPEG. The video id is resolved out of that. Verified on a device for all four: the Substack site URL adds "Letters from an American" and fetches 20 articles; a West Hawaii Today article extracts with no Trending headers and its body intact; the video plays; and a long-press reaches the YouTube app. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
Diffstat (limited to 'app/src/test/java/org/terst/feedreader/readability/EvalOutcomeTest.kt')
-rw-r--r--app/src/test/java/org/terst/feedreader/readability/EvalOutcomeTest.kt18
1 files changed, 18 insertions, 0 deletions
diff --git a/app/src/test/java/org/terst/feedreader/readability/EvalOutcomeTest.kt b/app/src/test/java/org/terst/feedreader/readability/EvalOutcomeTest.kt
index 8995e22..3e2b28f 100644
--- a/app/src/test/java/org/terst/feedreader/readability/EvalOutcomeTest.kt
+++ b/app/src/test/java/org/terst/feedreader/readability/EvalOutcomeTest.kt
@@ -1,6 +1,7 @@
package org.terst.feedreader.readability
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
@@ -86,4 +87,21 @@ class EvalOutcomeTest {
assertTrue(f.userMessage().lowercase().contains("cookie") || f.userMessage().lowercase().contains("consent"))
assertTrue(f.userMessage() != ExtractionFailure.NoContent.userMessage())
}
+
+ /**
+ * A fresh extraction is cleaned before it is accepted, so the widgets never reach the
+ * screen and never reach `articles.extractedContent` either.
+ */
+ @Test
+ fun `a successful extraction has its recirculation widgets stripped`() {
+ val body = "<p>" + "Real article sentence. ".repeat(40) + "</p>"
+ val widget = "<div data-mrf-recirculation=\"IL_x\"><p><h3><span>Trending</span></h3></p></div>"
+ val article = ExtractedArticle(title = "T", content = body + widget + body)
+
+ val decision = decideEvalOutcome(article, rawResult = "{}", attempt = 1, maxAttempts = 6)
+
+ val content = ((decision as EvalDecision.Done).result as ExtractionResult.Success).article.content!!
+ assertFalse("the stray header reached the caller: $content", content.contains("Trending"))
+ assertTrue(content.contains("Real article sentence."))
+ }
}