Converts the reader screen to a Robolectric JVM Compose test (umbrella #373, dispatch 6/9) and drops **/ReaderScreen* from jacocoNonJvmTestableSurface.
What
ReaderScreenJvmTest drives the realReaderViewModel over a mocked MailRepository / SettingsRepository via the v2 createComposeRule() — no emulator — in the test (JVM) source set. 15 tests covering:
Chrome: top bar title + back, Reply, overflow Reply all / Forward, Star, Delete.
Body branches: loading spinner, plain-text body + header (subject/sender/email), blank-body placeholder, load-error text.
Attachments: single row (no accordion), multi-attachment accordion collapse/expand, downloaded indicator, and the download-failure snackbar. Fixtures exercise formatSize (B/KB/MB/empty) and fileExtension (normal + FILE fallback).
Remote-images banner: shown for an HTML message, hidden after "Show images", and suppressed by the load-remote-images preference.
WebView caveat
ReaderScreen embeds HtmlBody — a hardened WebView via AndroidView. Under Robolectric the WebView is a non-rendering shadow, so no WebView-rendered HTML is asserted. The remote-images banner (which only appears for an HTML message) is driven with an HTML message whose body is blank, hitting the isHtml && !loadRemoteImages banner branch and the blank-body placeholder branch without invoking HtmlBody at all.
HtmlBody.kt is not part of this batch and stays in scope — its logic (cidKey/resolveInlineImage/wrapHtml/toCssHex) is already JVM-covered by HtmlBodyTest / InlineImageResolverTest, and its WebView render is not meaningfully JVM-testable.
Coverage
ReaderScreenKt: 94.3% line (265/281). The 16 missed lines are the intentionally-skipped HtmlBody(...) WebView call and the openAttachment FileProvider intent path.
Bundle line coverage rises to 82.7%, above the unchanged 0.79 floor (the re-ratchet stays the final #373 step).
Tests
New: app/src/test/kotlin/org/libremail/ui/reader/ReaderScreenJvmTest.kt (15 tests, all green).
The instrumented ReaderScreenTest stays as the on-device E2E.
Fast gate green locally: assembleDebug + testDebugUnitTest + jacocoTestCoverageVerification + compileDebugAndroidTestKotlin + lintDebug + ktlintCheck + detekt.
Converts the reader screen to a Robolectric JVM Compose test (umbrella #373, dispatch 6/9) and drops `**/ReaderScreen*` from `jacocoNonJvmTestableSurface`.
## What
`ReaderScreenJvmTest` drives the **real** `ReaderViewModel` over a mocked `MailRepository` / `SettingsRepository` via the v2 `createComposeRule()` — no emulator — in the `test` (JVM) source set. 15 tests covering:
- **Chrome:** top bar title + back, `Reply`, overflow `Reply all` / `Forward`, `Star`, `Delete`.
- **Body branches:** loading spinner, plain-text body + header (subject/sender/email), blank-body placeholder, load-error text.
- **Attachments:** single row (no accordion), multi-attachment accordion collapse/expand, downloaded indicator, and the download-failure snackbar. Fixtures exercise `formatSize` (B/KB/MB/empty) and `fileExtension` (normal + `FILE` fallback).
- **Remote-images banner:** shown for an HTML message, hidden after "Show images", and suppressed by the load-remote-images preference.
## WebView caveat
`ReaderScreen` embeds `HtmlBody` — a hardened `WebView` via `AndroidView`. Under Robolectric the `WebView` is a **non-rendering shadow**, so no WebView-rendered HTML is asserted. The remote-images banner (which only appears for an HTML message) is driven with an HTML message whose body is **blank**, hitting the `isHtml && !loadRemoteImages` banner branch and the blank-body placeholder branch **without invoking `HtmlBody`** at all.
`HtmlBody.kt` is **not** part of this batch and stays in scope — its logic (`cidKey`/`resolveInlineImage`/`wrapHtml`/`toCssHex`) is already JVM-covered by `HtmlBodyTest` / `InlineImageResolverTest`, and its `WebView` render is not meaningfully JVM-testable.
## Coverage
- `ReaderScreenKt`: **94.3%** line (265/281). The 16 missed lines are the intentionally-skipped `HtmlBody(...)` WebView call and the `openAttachment` FileProvider intent path.
- Bundle line coverage rises to **82.7%**, above the **unchanged** 0.79 floor (the re-ratchet stays the final #373 step).
## Tests
- New: `app/src/test/kotlin/org/libremail/ui/reader/ReaderScreenJvmTest.kt` (15 tests, all green).
- The instrumented `ReaderScreenTest` stays as the on-device E2E.
Fast gate green locally: `assembleDebug` + `testDebugUnitTest` + `jacocoTestCoverageVerification` + `compileDebugAndroidTestKotlin` + `lintDebug` + `ktlintCheck` + `detekt`.
Part of #373.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Converts the reader screen to a Robolectric JVM Compose test (umbrella #373, dispatch 6/9) and drops
**/ReaderScreen*fromjacocoNonJvmTestableSurface.What
ReaderScreenJvmTestdrives the realReaderViewModelover a mockedMailRepository/SettingsRepositoryvia the v2createComposeRule()— no emulator — in thetest(JVM) source set. 15 tests covering:Reply, overflowReply all/Forward,Star,Delete.formatSize(B/KB/MB/empty) andfileExtension(normal +FILEfallback).WebView caveat
ReaderScreenembedsHtmlBody— a hardenedWebViewviaAndroidView. Under Robolectric theWebViewis a non-rendering shadow, so no WebView-rendered HTML is asserted. The remote-images banner (which only appears for an HTML message) is driven with an HTML message whose body is blank, hitting theisHtml && !loadRemoteImagesbanner branch and the blank-body placeholder branch without invokingHtmlBodyat all.HtmlBody.ktis not part of this batch and stays in scope — its logic (cidKey/resolveInlineImage/wrapHtml/toCssHex) is already JVM-covered byHtmlBodyTest/InlineImageResolverTest, and itsWebViewrender is not meaningfully JVM-testable.Coverage
ReaderScreenKt: 94.3% line (265/281). The 16 missed lines are the intentionally-skippedHtmlBody(...)WebView call and theopenAttachmentFileProvider intent path.Tests
app/src/test/kotlin/org/libremail/ui/reader/ReaderScreenJvmTest.kt(15 tests, all green).ReaderScreenTeststays as the on-device E2E.Fast gate green locally:
assembleDebug+testDebugUnitTest+jacocoTestCoverageVerification+compileDebugAndroidTestKotlin+lintDebug+ktlintCheck+detekt.Part of #373.
🤖 Generated with Claude Code