summaryrefslogtreecommitdiff
path: root/app/src/test/java/org/terst/feedreader/readability/EvalOutcomeTest.kt
blob: 8995e222730fbb3ae0ba8bfbea7d3513729f510f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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())
    }
}