diff options
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.kt | 89 |
1 files changed, 89 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 new file mode 100644 index 0000000..8995e22 --- /dev/null +++ b/app/src/test/java/org/terst/feedreader/readability/EvalOutcomeTest.kt @@ -0,0 +1,89 @@ +package org.terst.feedreader.readability + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +/** + * The decision ReaderExtractor makes about each `evaluateJavascript` result: + * accept it, inject again because the page probably was not ready, or give up + * with a reason. It used to live inline in a WebView callback, which meant the + * only way to exercise it was on a device against a live page. + * + * The case that mattered and was never covered: a result that is perfectly + * well-formed and non-blank, and is the page's cookie-consent banner rather than + * the article. That counted as a success, got cached, and stayed cached. + */ +class EvalOutcomeTest { + + private val maxAttempts = 6 + + private fun decide(article: ExtractedArticle?, attempt: Int, raw: String = "\"{}\"") = + decideEvalOutcome(article, raw, attempt, maxAttempts) + + private fun article(content: String?, error: String? = null) = + ExtractedArticle(title = "t", content = content, textContent = content, error = error) + + @Test + fun `real content is accepted on the first attempt`() { + val a = article("<p>" + "The actual article body. ".repeat(60) + "</p>") + val decision = decide(a, attempt = 1) + assertEquals(EvalDecision.Done(ExtractionResult.Success(a)), decision) + } + + // Retrying cannot change Readability's opinion of the markup, so a thrown + // error is final however early it arrives. + @Test + fun `a readability error is final immediately`() { + val decision = decide(article(null, error = "TypeError: x is null"), attempt = 1) + assertEquals( + EvalDecision.Done(ExtractionResult.Failure(ExtractionFailure.ReadabilityError("TypeError: x is null"))), + decision + ) + } + + @Test + fun `an undecodable result retries and then reports a bad result`() { + assertEquals(EvalDecision.Retry, decide(null, attempt = 1, raw = "null")) + val last = decide(null, attempt = maxAttempts, raw = "null") + assertEquals(EvalDecision.Done(ExtractionResult.Failure(ExtractionFailure.BadResult("null"))), last) + } + + @Test + fun `blank content retries and then reports no content`() { + assertEquals(EvalDecision.Retry, decide(article(""), attempt = 1)) + assertEquals( + EvalDecision.Done(ExtractionResult.Failure(ExtractionFailure.NoContent)), + decide(article(""), attempt = maxAttempts) + ) + } + + // A cookie banner is exactly what a half-loaded page yields -- the banner is + // injected early, the article body may not be in the DOM yet -- so the right + // first move is the retry the extractor already does for an empty result. + @Test + fun `a consent notice retries rather than being accepted`() { + val decision = decide(article(ConsentNoticeTest.EXTRACTED_COOKIE_NOTICE), attempt = 1) + assertEquals(EvalDecision.Retry, decision) + } + + @Test + fun `a consent notice that survives every attempt is reported as a failure`() { + val decision = decide(article(ConsentNoticeTest.EXTRACTED_COOKIE_NOTICE), attempt = maxAttempts) + assertEquals( + EvalDecision.Done(ExtractionResult.Failure(ExtractionFailure.ConsentInterstitial)), + decision + ) + } + + // The failure has to be distinguishable from "no content": one means the page + // has no article, the other means the page would not show it without consent. + @Test + fun `the consent failure says what happened`() { + val f: ExtractionFailure = ExtractionFailure.ConsentInterstitial + assertTrue(f.userMessage().isNotBlank()) + assertTrue(f.diagnostic().isNotBlank()) + assertTrue(f.userMessage().lowercase().contains("cookie") || f.userMessage().lowercase().contains("consent")) + assertTrue(f.userMessage() != ExtractionFailure.NoContent.userMessage()) + } +} |
